mirror of
https://github.com/majonezz/solarlife.git
synced 2026-03-28 18:42:34 +01:00
Initial entry
This commit is contained in:
58
README.md
58
README.md
@@ -1,2 +1,56 @@
|
||||
# solarlife
|
||||
Get data from cheap solar charge controller and pass it to MQTT broker
|
||||
#Solarlife
|
||||
|
||||
This is a simple approach to read, parse and pass data from cheap solar charge controllers like Lumiax and Volt to MQTT broker.
|
||||
You can pass data to home automation software like Home Assistant, Openhab and others.
|
||||
|
||||
Parameters that you will be able to get are for example:
|
||||
|
||||
-battery voltage
|
||||
-battery current
|
||||
-battery power
|
||||
-battery percentage capability
|
||||
-solar voltage
|
||||
-solar current
|
||||
-solar power
|
||||
-load voltage
|
||||
-load current
|
||||
-load power
|
||||
-and many more...
|
||||
|
||||
|
||||
Based on:
|
||||
https://github.com/majki09/lumiax_solar_bt/tree/main
|
||||
https://github.com/gbrault/gattclient/tree/master
|
||||
https://ctlsys.com/support/how_to_compute_the_modbus_rtu_message_crc/
|
||||
https://github.com/kokke/tiny-MQTT-c
|
||||
https://github.com/rpz80/json
|
||||
Lumiax Modbus Communication Protocol V3.9-1.pdf
|
||||
|
||||
Depends on:
|
||||
libglib-2.0
|
||||
|
||||
Tested on VM running Ubuntu 21.04 and Fritz-Box 6690 Cable router.
|
||||
|
||||
To use this app you will need a Bluetooth adapter with low energy capability and compatible solar charger with Bluetooth.
|
||||
Generally speaking all of charging controllers that uses "Solar Life" app for Android should work.
|
||||
|
||||
|
||||
TODO:
|
||||
-Fix bugs
|
||||
-Pass commands to controller (load on/off, clock setup, etc.)
|
||||
|
||||
Example output:
|
||||
|
||||
root@ubuntu:/usr/src/solarlife# ./solarlife -d 04:7f:0e:51:21:4c -a test.mqtt.com
|
||||
Connecting to BT device... Done
|
||||
MQTT: connected to server.
|
||||
Service Added - UUID: 00001801-0000-1000-8000-00805f9b34fb start: 0x0001 end: 0x0003
|
||||
Service Added - UUID: 00001800-0000-1000-8000-00805f9b34fb start: 0x0004 end: 0x000e
|
||||
Service Added - UUID: 0000ff00-0000-1000-8000-00805f9b34fb start: 0x000f end: 0x001a
|
||||
GATT discovery procedures complete
|
||||
Registering notify handler with id: 2
|
||||
Registered notify handler!
|
||||
{"PV_rated_voltage":50,"PV_rated_current":10,"PV_rated_power_l":130,"PV_rated_power_h":0,"battery_rated_voltage":17,"battery_rated_current":10,"battery_rated_power_l":130,"battery_rated_power_h":0,"load_rated_voltage":17,"load_rated_current":10,"load_rated_power_l":130,"load_rated_power_h":0,"slave_id":1,"running_days":6,"sys_voltage":12,"battery_status":3,"charge_status":32,"discharge_status":0,"env_temperature":13,"sys_temperature":6,"undervoltage_times":255,"fullycharged_times":1,"overvoltage_prot_times":0,"overcurrent_prot_times":0,"shortcircuit_prot_times":0,"opencircuit_prot_times":0,"hw_prot_times":0,"charge_overtemp_prot_times":0,"discharge_overtemp_prot_times":0,"battery_remaining_capacity":16,"battery_voltage":11.13,"battery_current":0,"battery_power_lo":0,"battery_power_hi":0,"load_voltage":0,"load_current":0,"load_power_l":0,"load_power_h":0,"solar_voltage":0.2,"solar_current":0,"solar_power_l":0,"solar_power_h":0,"daily_production":0.01,"total_production_l":0.82,"total_production_h":0,"daily_consumption":0.02,"total_consumption_l":0.83,"total_consumption_h":0,"lighttime_daily":695,"monthly_production_l":0,"monthly_production_h":0,"yearly_production_l":0,"yearly_production_h":0,"timestamp":"2023-10-20T19:15:58Z"}
|
||||
MQTT server acknowledged data
|
||||
|
||||
|
||||
|
||||
147
att-types.h
Normal file
147
att-types.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2014 Google Inc.
|
||||
*
|
||||
*
|
||||
* This library 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 library 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 this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef __packed
|
||||
#define __packed __attribute__((packed))
|
||||
#endif
|
||||
|
||||
#define BT_ATT_SECURITY_AUTO 0
|
||||
#define BT_ATT_SECURITY_LOW 1
|
||||
#define BT_ATT_SECURITY_MEDIUM 2
|
||||
#define BT_ATT_SECURITY_HIGH 3
|
||||
|
||||
#define BT_ATT_DEFAULT_LE_MTU 23
|
||||
#define BT_ATT_MAX_LE_MTU 517
|
||||
#define BT_ATT_MAX_VALUE_LEN 512
|
||||
|
||||
/* ATT protocol opcodes */
|
||||
#define BT_ATT_OP_ERROR_RSP 0x01
|
||||
#define BT_ATT_OP_MTU_REQ 0x02
|
||||
#define BT_ATT_OP_MTU_RSP 0x03
|
||||
#define BT_ATT_OP_FIND_INFO_REQ 0x04
|
||||
#define BT_ATT_OP_FIND_INFO_RSP 0x05
|
||||
#define BT_ATT_OP_FIND_BY_TYPE_VAL_REQ 0x06
|
||||
#define BT_ATT_OP_FIND_BY_TYPE_VAL_RSP 0x07
|
||||
#define BT_ATT_OP_READ_BY_TYPE_REQ 0x08
|
||||
#define BT_ATT_OP_READ_BY_TYPE_RSP 0x09
|
||||
#define BT_ATT_OP_READ_REQ 0x0a
|
||||
#define BT_ATT_OP_READ_RSP 0x0b
|
||||
#define BT_ATT_OP_READ_BLOB_REQ 0x0c
|
||||
#define BT_ATT_OP_READ_BLOB_RSP 0x0d
|
||||
#define BT_ATT_OP_READ_MULT_REQ 0x0e
|
||||
#define BT_ATT_OP_READ_MULT_RSP 0x0f
|
||||
#define BT_ATT_OP_READ_BY_GRP_TYPE_REQ 0x10
|
||||
#define BT_ATT_OP_READ_BY_GRP_TYPE_RSP 0x11
|
||||
#define BT_ATT_OP_WRITE_REQ 0x12
|
||||
#define BT_ATT_OP_WRITE_RSP 0x13
|
||||
#define BT_ATT_OP_WRITE_CMD 0x52
|
||||
#define BT_ATT_OP_SIGNED_WRITE_CMD 0xD2
|
||||
#define BT_ATT_OP_PREP_WRITE_REQ 0x16
|
||||
#define BT_ATT_OP_PREP_WRITE_RSP 0x17
|
||||
#define BT_ATT_OP_EXEC_WRITE_REQ 0x18
|
||||
#define BT_ATT_OP_EXEC_WRITE_RSP 0x19
|
||||
#define BT_ATT_OP_HANDLE_VAL_NOT 0x1B
|
||||
#define BT_ATT_OP_HANDLE_VAL_IND 0x1D
|
||||
#define BT_ATT_OP_HANDLE_VAL_CONF 0x1E
|
||||
|
||||
/* Packed struct definitions for ATT protocol PDUs */
|
||||
/* TODO: Complete these definitions for all opcodes */
|
||||
struct bt_att_pdu_error_rsp {
|
||||
uint8_t opcode;
|
||||
uint16_t handle;
|
||||
uint8_t ecode;
|
||||
} __packed;
|
||||
|
||||
/* Special opcode to receive all requests (legacy servers) */
|
||||
#define BT_ATT_ALL_REQUESTS 0x00
|
||||
|
||||
/* Error codes for Error response PDU */
|
||||
#define BT_ATT_ERROR_INVALID_HANDLE 0x01
|
||||
#define BT_ATT_ERROR_READ_NOT_PERMITTED 0x02
|
||||
#define BT_ATT_ERROR_WRITE_NOT_PERMITTED 0x03
|
||||
#define BT_ATT_ERROR_INVALID_PDU 0x04
|
||||
#define BT_ATT_ERROR_AUTHENTICATION 0x05
|
||||
#define BT_ATT_ERROR_REQUEST_NOT_SUPPORTED 0x06
|
||||
#define BT_ATT_ERROR_INVALID_OFFSET 0x07
|
||||
#define BT_ATT_ERROR_AUTHORIZATION 0x08
|
||||
#define BT_ATT_ERROR_PREPARE_QUEUE_FULL 0x09
|
||||
#define BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND 0x0A
|
||||
#define BT_ATT_ERROR_ATTRIBUTE_NOT_LONG 0x0B
|
||||
#define BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE 0x0C
|
||||
#define BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN 0x0D
|
||||
#define BT_ATT_ERROR_UNLIKELY 0x0E
|
||||
#define BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION 0x0F
|
||||
#define BT_ATT_ERROR_UNSUPPORTED_GROUP_TYPE 0x10
|
||||
#define BT_ATT_ERROR_INSUFFICIENT_RESOURCES 0x11
|
||||
|
||||
/*
|
||||
* Common Profile and Service Error Code descriptions (see Supplement to the
|
||||
* Bluetooth Core Specification, sections 1.2 and 2). The error codes within
|
||||
* 0xE0-0xFC are reserved for future use. The remaining 3 are defined as the
|
||||
* following:
|
||||
*/
|
||||
#define BT_ERROR_CCC_IMPROPERLY_CONFIGURED 0xfd
|
||||
#define BT_ERROR_ALREADY_IN_PROGRESS 0xfe
|
||||
#define BT_ERROR_OUT_OF_RANGE 0xff
|
||||
|
||||
/*
|
||||
* ATT attribute permission bitfield values. Permissions are grouped as
|
||||
* "Access", "Encryption", "Authentication", and "Authorization". A bitmask of
|
||||
* permissions is a byte that encodes a combination of these.
|
||||
*/
|
||||
#define BT_ATT_PERM_READ 0x01
|
||||
#define BT_ATT_PERM_WRITE 0x02
|
||||
#define BT_ATT_PERM_READ_ENCRYPT 0x04
|
||||
#define BT_ATT_PERM_WRITE_ENCRYPT 0x08
|
||||
#define BT_ATT_PERM_ENCRYPT (BT_ATT_PERM_READ_ENCRYPT | \
|
||||
BT_ATT_PERM_WRITE_ENCRYPT)
|
||||
#define BT_ATT_PERM_READ_AUTHEN 0x10
|
||||
#define BT_ATT_PERM_WRITE_AUTHEN 0x20
|
||||
#define BT_ATT_PERM_AUTHEN (BT_ATT_PERM_READ_AUTHEN | \
|
||||
BT_ATT_PERM_WRITE_AUTHEN)
|
||||
#define BT_ATT_PERM_AUTHOR 0x40
|
||||
#define BT_ATT_PERM_NONE 0x80
|
||||
|
||||
/* GATT Characteristic Properties Bitfield values */
|
||||
#define BT_GATT_CHRC_PROP_BROADCAST 0x01
|
||||
#define BT_GATT_CHRC_PROP_READ 0x02
|
||||
#define BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP 0x04
|
||||
#define BT_GATT_CHRC_PROP_WRITE 0x08
|
||||
#define BT_GATT_CHRC_PROP_NOTIFY 0x10
|
||||
#define BT_GATT_CHRC_PROP_INDICATE 0x20
|
||||
#define BT_GATT_CHRC_PROP_AUTH 0x40
|
||||
#define BT_GATT_CHRC_PROP_EXT_PROP 0x80
|
||||
|
||||
/* GATT Characteristic Extended Properties Bitfield values */
|
||||
#define BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE 0x01
|
||||
#define BT_GATT_CHRC_EXT_PROP_WRITABLE_AUX 0x02
|
||||
#define BT_GATT_CHRC_EXT_PROP_ENC_READ 0x04
|
||||
#define BT_GATT_CHRC_EXT_PROP_ENC_WRITE 0x08
|
||||
#define BT_GATT_CHRC_EXT_PROP_ENC (BT_GATT_CHRC_EXT_PROP_ENC_READ | \
|
||||
BT_GATT_CHRC_EXT_PROP_ENC_WRITE)
|
||||
#define BT_GATT_CHRC_EXT_PROP_AUTH_READ 0x10
|
||||
#define BT_GATT_CHRC_EXT_PROP_AUTH_WRITE 0x20
|
||||
#define BT_GATT_CHRC_EXT_PROP_AUTH (BT_GATT_CHRC_EXT_PROP_AUTH_READ | \
|
||||
BT_GATT_CHRC_EXT_PROP_AUTH_WRITE)
|
||||
93
att.h
Normal file
93
att.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2014 Google Inc.
|
||||
*
|
||||
*
|
||||
* This library 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 library 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 this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "att-types.h"
|
||||
|
||||
struct bt_att;
|
||||
|
||||
struct bt_att *bt_att_new(int fd, bool ext_signed);
|
||||
|
||||
struct bt_att *bt_att_ref(struct bt_att *att);
|
||||
void bt_att_unref(struct bt_att *att);
|
||||
|
||||
bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close);
|
||||
|
||||
int bt_att_get_fd(struct bt_att *att);
|
||||
|
||||
typedef void (*bt_att_response_func_t)(uint8_t opcode, const void *pdu,
|
||||
uint16_t length, void *user_data);
|
||||
typedef void (*bt_att_notify_func_t)(uint8_t opcode, const void *pdu,
|
||||
uint16_t length, void *user_data);
|
||||
typedef void (*bt_att_destroy_func_t)(void *user_data);
|
||||
typedef void (*bt_att_debug_func_t)(const char *str, void *user_data);
|
||||
typedef void (*bt_att_timeout_func_t)(unsigned int id, uint8_t opcode,
|
||||
void *user_data);
|
||||
typedef void (*bt_att_disconnect_func_t)(int err, void *user_data);
|
||||
typedef bool (*bt_att_counter_func_t)(uint32_t *sign_cnt, void *user_data);
|
||||
|
||||
bool bt_att_set_debug(struct bt_att *att, bt_att_debug_func_t callback,
|
||||
void *user_data, bt_att_destroy_func_t destroy);
|
||||
|
||||
uint16_t bt_att_get_mtu(struct bt_att *att);
|
||||
bool bt_att_set_mtu(struct bt_att *att, uint16_t mtu);
|
||||
|
||||
bool bt_att_set_timeout_cb(struct bt_att *att, bt_att_timeout_func_t callback,
|
||||
void *user_data,
|
||||
bt_att_destroy_func_t destroy);
|
||||
|
||||
unsigned int bt_att_send(struct bt_att *att, uint8_t opcode,
|
||||
const void *pdu, uint16_t length,
|
||||
bt_att_response_func_t callback,
|
||||
void *user_data,
|
||||
bt_att_destroy_func_t destroy);
|
||||
bool bt_att_cancel(struct bt_att *att, unsigned int id);
|
||||
bool bt_att_cancel_all(struct bt_att *att);
|
||||
|
||||
unsigned int bt_att_send_error_rsp(struct bt_att *att, uint8_t opcode,
|
||||
uint16_t handle, int error);
|
||||
|
||||
unsigned int bt_att_register(struct bt_att *att, uint8_t opcode,
|
||||
bt_att_notify_func_t callback,
|
||||
void *user_data,
|
||||
bt_att_destroy_func_t destroy);
|
||||
bool bt_att_unregister(struct bt_att *att, unsigned int id);
|
||||
|
||||
unsigned int bt_att_register_disconnect(struct bt_att *att,
|
||||
bt_att_disconnect_func_t callback,
|
||||
void *user_data,
|
||||
bt_att_destroy_func_t destroy);
|
||||
bool bt_att_unregister_disconnect(struct bt_att *att, unsigned int id);
|
||||
|
||||
bool bt_att_unregister_all(struct bt_att *att);
|
||||
|
||||
int bt_att_get_security(struct bt_att *att);
|
||||
bool bt_att_set_security(struct bt_att *att, int level);
|
||||
|
||||
bool bt_att_set_local_key(struct bt_att *att, uint8_t sign_key[16],
|
||||
bt_att_counter_func_t func, void *user_data);
|
||||
bool bt_att_set_remote_key(struct bt_att *att, uint8_t sign_key[16],
|
||||
bt_att_counter_func_t func, void *user_data);
|
||||
bool bt_att_has_crypto(struct bt_att *att);
|
||||
1456
bluetooth.c
Normal file
1456
bluetooth.c
Normal file
File diff suppressed because it is too large
Load Diff
403
bluetooth.h
Normal file
403
bluetooth.h
Normal file
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2000-2001 Qualcomm Incorporated
|
||||
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
* Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __BLUETOOTH_H
|
||||
#define __BLUETOOTH_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <endian.h>
|
||||
#include <byteswap.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#ifndef AF_BLUETOOTH
|
||||
#define AF_BLUETOOTH 31
|
||||
#define PF_BLUETOOTH AF_BLUETOOTH
|
||||
#endif
|
||||
|
||||
#define BTPROTO_L2CAP 0
|
||||
#define BTPROTO_HCI 1
|
||||
#define BTPROTO_SCO 2
|
||||
#define BTPROTO_RFCOMM 3
|
||||
#define BTPROTO_BNEP 4
|
||||
#define BTPROTO_CMTP 5
|
||||
#define BTPROTO_HIDP 6
|
||||
#define BTPROTO_AVDTP 7
|
||||
|
||||
#define SOL_HCI 0
|
||||
#define SOL_L2CAP 6
|
||||
#define SOL_SCO 17
|
||||
#define SOL_RFCOMM 18
|
||||
|
||||
#ifndef SOL_BLUETOOTH
|
||||
#define SOL_BLUETOOTH 274
|
||||
#endif
|
||||
|
||||
#define BT_SECURITY 4
|
||||
struct bt_security {
|
||||
uint8_t level;
|
||||
uint8_t key_size;
|
||||
};
|
||||
#define BT_SECURITY_SDP 0
|
||||
#define BT_SECURITY_LOW 1
|
||||
#define BT_SECURITY_MEDIUM 2
|
||||
#define BT_SECURITY_HIGH 3
|
||||
|
||||
#define BT_DEFER_SETUP 7
|
||||
|
||||
#define BT_FLUSHABLE 8
|
||||
|
||||
#define BT_FLUSHABLE_OFF 0
|
||||
#define BT_FLUSHABLE_ON 1
|
||||
|
||||
#define BT_POWER 9
|
||||
struct bt_power {
|
||||
uint8_t force_active;
|
||||
};
|
||||
#define BT_POWER_FORCE_ACTIVE_OFF 0
|
||||
#define BT_POWER_FORCE_ACTIVE_ON 1
|
||||
|
||||
#define BT_CHANNEL_POLICY 10
|
||||
|
||||
/* BR/EDR only (default policy)
|
||||
* AMP controllers cannot be used.
|
||||
* Channel move requests from the remote device are denied.
|
||||
* If the L2CAP channel is currently using AMP, move the channel to BR/EDR.
|
||||
*/
|
||||
#define BT_CHANNEL_POLICY_BREDR_ONLY 0
|
||||
|
||||
/* BR/EDR Preferred
|
||||
* Allow use of AMP controllers.
|
||||
* If the L2CAP channel is currently on AMP, move it to BR/EDR.
|
||||
* Channel move requests from the remote device are allowed.
|
||||
*/
|
||||
#define BT_CHANNEL_POLICY_BREDR_PREFERRED 1
|
||||
|
||||
/* AMP Preferred
|
||||
* Allow use of AMP controllers
|
||||
* If the L2CAP channel is currently on BR/EDR and AMP controller
|
||||
* resources are available, initiate a channel move to AMP.
|
||||
* Channel move requests from the remote device are allowed.
|
||||
* If the L2CAP socket has not been connected yet, try to create
|
||||
* and configure the channel directly on an AMP controller rather
|
||||
* than BR/EDR.
|
||||
*/
|
||||
#define BT_CHANNEL_POLICY_AMP_PREFERRED 2
|
||||
|
||||
#define BT_VOICE 11
|
||||
struct bt_voice {
|
||||
uint16_t setting;
|
||||
};
|
||||
|
||||
#define BT_SNDMTU 12
|
||||
#define BT_RCVMTU 13
|
||||
|
||||
#define BT_VOICE_TRANSPARENT 0x0003
|
||||
#define BT_VOICE_CVSD_16BIT 0x0060
|
||||
|
||||
/* Connection and socket states */
|
||||
enum {
|
||||
BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */
|
||||
BT_OPEN,
|
||||
BT_BOUND,
|
||||
BT_LISTEN,
|
||||
BT_CONNECT,
|
||||
BT_CONNECT2,
|
||||
BT_CONFIG,
|
||||
BT_DISCONN,
|
||||
BT_CLOSED
|
||||
};
|
||||
|
||||
/* Byte order conversions */
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define htobs(d) (d)
|
||||
#define htobl(d) (d)
|
||||
#define htobll(d) (d)
|
||||
#define btohs(d) (d)
|
||||
#define btohl(d) (d)
|
||||
#define btohll(d) (d)
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
#define htobs(d) bswap_16(d)
|
||||
#define htobl(d) bswap_32(d)
|
||||
#define htobll(d) bswap_64(d)
|
||||
#define btohs(d) bswap_16(d)
|
||||
#define btohl(d) bswap_32(d)
|
||||
#define btohll(d) bswap_64(d)
|
||||
#else
|
||||
#error "Unknown byte order"
|
||||
#endif
|
||||
|
||||
/* Bluetooth unaligned access */
|
||||
#define bt_get_unaligned(ptr) \
|
||||
__extension__ ({ \
|
||||
struct __attribute__((packed)) { \
|
||||
__typeof__(*(ptr)) __v; \
|
||||
} *__p = (__typeof__(__p)) (ptr); \
|
||||
__p->__v; \
|
||||
})
|
||||
|
||||
#define bt_put_unaligned(val, ptr) \
|
||||
do { \
|
||||
struct __attribute__((packed)) { \
|
||||
__typeof__(*(ptr)) __v; \
|
||||
} *__p = (__typeof__(__p)) (ptr); \
|
||||
__p->__v = (val); \
|
||||
} while(0)
|
||||
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
static inline uint64_t bt_get_le64(const void *ptr)
|
||||
{
|
||||
return bt_get_unaligned((const uint64_t *) ptr);
|
||||
}
|
||||
|
||||
static inline uint64_t bt_get_be64(const void *ptr)
|
||||
{
|
||||
return bswap_64(bt_get_unaligned((const uint64_t *) ptr));
|
||||
}
|
||||
|
||||
static inline uint32_t bt_get_le32(const void *ptr)
|
||||
{
|
||||
return bt_get_unaligned((const uint32_t *) ptr);
|
||||
}
|
||||
|
||||
static inline uint32_t bt_get_be32(const void *ptr)
|
||||
{
|
||||
return bswap_32(bt_get_unaligned((const uint32_t *) ptr));
|
||||
}
|
||||
|
||||
static inline uint16_t bt_get_le16(const void *ptr)
|
||||
{
|
||||
return bt_get_unaligned((const uint16_t *) ptr);
|
||||
}
|
||||
|
||||
static inline uint16_t bt_get_be16(const void *ptr)
|
||||
{
|
||||
return bswap_16(bt_get_unaligned((const uint16_t *) ptr));
|
||||
}
|
||||
|
||||
static inline void bt_put_le64(uint64_t val, const void *ptr)
|
||||
{
|
||||
bt_put_unaligned(val, (uint64_t *) ptr);
|
||||
}
|
||||
|
||||
static inline void bt_put_be64(uint64_t val, const void *ptr)
|
||||
{
|
||||
bt_put_unaligned(bswap_64(val), (uint64_t *) ptr);
|
||||
}
|
||||
|
||||
static inline void bt_put_le32(uint32_t val, const void *ptr)
|
||||
{
|
||||
bt_put_unaligned(val, (uint32_t *) ptr);
|
||||
}
|
||||
|
||||
static inline void bt_put_be32(uint32_t val, const void *ptr)
|
||||
{
|
||||
bt_put_unaligned(bswap_32(val), (uint32_t *) ptr);
|
||||
}
|
||||
|
||||
static inline void bt_put_le16(uint16_t val, const void *ptr)
|
||||
{
|
||||
bt_put_unaligned(val, (uint16_t *) ptr);
|
||||
}
|
||||
|
||||
static inline void bt_put_be16(uint16_t val, const void *ptr)
|
||||
{
|
||||
bt_put_unaligned(bswap_16(val), (uint16_t *) ptr);
|
||||
}
|
||||
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
static inline uint64_t bt_get_le64(const void *ptr)
|
||||
{
|
||||
return bswap_64(bt_get_unaligned((const uint64_t *) ptr));
|
||||
}
|
||||
|
||||
static inline uint64_t bt_get_be64(const void *ptr)
|
||||
{
|
||||
return bt_get_unaligned((const uint64_t *) ptr);
|
||||
}
|
||||
|
||||
static inline uint32_t bt_get_le32(const void *ptr)
|
||||
{
|
||||
return bswap_32(bt_get_unaligned((const uint32_t *) ptr));
|
||||
}
|
||||
|
||||
static inline uint32_t bt_get_be32(const void *ptr)
|
||||
{
|
||||
return bt_get_unaligned((const uint32_t *) ptr);
|
||||
}
|
||||
|
||||
static inline uint16_t bt_get_le16(const void *ptr)
|
||||
{
|
||||
return bswap_16(bt_get_unaligned((const uint16_t *) ptr));
|
||||
}
|
||||
|
||||
static inline uint16_t bt_get_be16(const void *ptr)
|
||||
{
|
||||
return bt_get_unaligned((const uint16_t *) ptr);
|
||||
}
|
||||
|
||||
static inline void bt_put_le64(uint64_t val, const void *ptr)
|
||||
{
|
||||
bt_put_unaligned(bswap_64(val), (uint64_t *) ptr);
|
||||
}
|
||||
|
||||
static inline void bt_put_be64(uint64_t val, const void *ptr)
|
||||
{
|
||||
bt_put_unaligned(val, (uint64_t *) ptr);
|
||||
}
|
||||
|
||||
static inline void bt_put_le32(uint32_t val, const void *ptr)
|
||||
{
|
||||
bt_put_unaligned(bswap_32(val), (uint32_t *) ptr);
|
||||
}
|
||||
|
||||
static inline void bt_put_be32(uint32_t val, const void *ptr)
|
||||
{
|
||||
bt_put_unaligned(val, (uint32_t *) ptr);
|
||||
}
|
||||
|
||||
static inline void bt_put_le16(uint16_t val, const void *ptr)
|
||||
{
|
||||
bt_put_unaligned(bswap_16(val), (uint16_t *) ptr);
|
||||
}
|
||||
|
||||
static inline void bt_put_be16(uint16_t val, const void *ptr)
|
||||
{
|
||||
bt_put_unaligned(val, (uint16_t *) ptr);
|
||||
}
|
||||
#else
|
||||
#error "Unknown byte order"
|
||||
#endif
|
||||
|
||||
/* BD Address */
|
||||
typedef struct {
|
||||
uint8_t b[6];
|
||||
} __attribute__((packed)) bdaddr_t;
|
||||
|
||||
/* BD Address type */
|
||||
#define BDADDR_BREDR 0x00
|
||||
#define BDADDR_LE_PUBLIC 0x01
|
||||
#define BDADDR_LE_RANDOM 0x02
|
||||
|
||||
#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}})
|
||||
#define BDADDR_ALL (&(bdaddr_t) {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}})
|
||||
#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}})
|
||||
|
||||
/* Copy, swap, convert BD Address */
|
||||
static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2)
|
||||
{
|
||||
return memcmp(ba1, ba2, sizeof(bdaddr_t));
|
||||
}
|
||||
static inline void bacpy(bdaddr_t *dst, const bdaddr_t *src)
|
||||
{
|
||||
memcpy(dst, src, sizeof(bdaddr_t));
|
||||
}
|
||||
|
||||
void baswap(bdaddr_t *dst, const bdaddr_t *src);
|
||||
bdaddr_t *strtoba(const char *str);
|
||||
char *batostr(const bdaddr_t *ba);
|
||||
int ba2str(const bdaddr_t *ba, char *str);
|
||||
int str2ba(const char *str, bdaddr_t *ba);
|
||||
int ba2oui(const bdaddr_t *ba, char *oui);
|
||||
int bachk(const char *str);
|
||||
|
||||
int baprintf(const char *format, ...);
|
||||
int bafprintf(FILE *stream, const char *format, ...);
|
||||
int basprintf(char *str, const char *format, ...);
|
||||
int basnprintf(char *str, size_t size, const char *format, ...);
|
||||
|
||||
void *bt_malloc(size_t size);
|
||||
void bt_free(void *ptr);
|
||||
|
||||
int bt_error(uint16_t code);
|
||||
const char *bt_compidtostr(int id);
|
||||
|
||||
typedef struct {
|
||||
uint8_t data[16];
|
||||
} uint128_t;
|
||||
|
||||
static inline void bswap_128(const void *src, void *dst)
|
||||
{
|
||||
const uint8_t *s = (const uint8_t *) src;
|
||||
uint8_t *d = (uint8_t *) dst;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
d[15 - i] = s[i];
|
||||
}
|
||||
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
|
||||
#define ntoh64(x) (x)
|
||||
|
||||
static inline void ntoh128(const uint128_t *src, uint128_t *dst)
|
||||
{
|
||||
memcpy(dst, src, sizeof(uint128_t));
|
||||
}
|
||||
|
||||
static inline void btoh128(const uint128_t *src, uint128_t *dst)
|
||||
{
|
||||
bswap_128(src, dst);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline uint64_t ntoh64(uint64_t n)
|
||||
{
|
||||
uint64_t h;
|
||||
uint64_t tmp = ntohl(n & 0x00000000ffffffff);
|
||||
|
||||
h = ntohl(n >> 32);
|
||||
h |= tmp << 32;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static inline void ntoh128(const uint128_t *src, uint128_t *dst)
|
||||
{
|
||||
bswap_128(src, dst);
|
||||
}
|
||||
|
||||
static inline void btoh128(const uint128_t *src, uint128_t *dst)
|
||||
{
|
||||
memcpy(dst, src, sizeof(uint128_t));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define hton64(x) ntoh64(x)
|
||||
#define hton128(x, y) ntoh128(x, y)
|
||||
#define htob128(x, y) btoh128(x, y)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __BLUETOOTH_H */
|
||||
249
client.c
Normal file
249
client.c
Normal file
@@ -0,0 +1,249 @@
|
||||
#include "client.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
|
||||
|
||||
/* Helper functions: */
|
||||
static void _dummy_connect (client_t* s, int f, const char* b) { (void) s; (void) f; (void) b; }
|
||||
static void _dummy_recv_data(client_t* s, int f, char* d, int l) { (void) s; (void) f; (void) d; (void) l; }
|
||||
|
||||
static void _change_state(client_t* psClnt, conn_state_t new_state)
|
||||
{
|
||||
require(psClnt != 0);
|
||||
|
||||
psClnt->state = new_state;
|
||||
psClnt->last_active = time(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Implementation of exported interface begins here
|
||||
*/
|
||||
|
||||
void client_init(client_t* psClnt, char* dst_addr, uint16_t dst_port, char* rxbuf, uint32_t rxbufsize)
|
||||
{
|
||||
require(psClnt != 0);
|
||||
require(dst_addr != 0);
|
||||
require(rxbuf != 0);
|
||||
|
||||
strcpy(psClnt->addr, dst_addr);
|
||||
psClnt->port = dst_port;
|
||||
psClnt->sockfd = 0;
|
||||
psClnt->rxbuf = rxbuf;
|
||||
psClnt->rxbufsz = rxbufsize;
|
||||
psClnt->client_connected = (void*)_dummy_connect;
|
||||
psClnt->client_disconnected = (void*)_dummy_connect;
|
||||
psClnt->client_new_data = (void*)_dummy_recv_data;
|
||||
|
||||
/*
|
||||
Set the socket I/O mode: In this case FIONBIO
|
||||
enables or disables the blocking mode for the
|
||||
socket based on the numerical value of iMode.
|
||||
If iMode = 0, blocking is enabled;
|
||||
If iMode != 0, non-blocking mode is enabled.
|
||||
*/
|
||||
int iMode = 0;
|
||||
ioctl(psClnt->sockfd, FIONBIO, &iMode);
|
||||
|
||||
/*
|
||||
// WINDOWS
|
||||
DWORD timeout = SOCKET_READ_TIMEOUT_SEC * 1000;
|
||||
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
|
||||
*/
|
||||
|
||||
_change_state(psClnt, CREATED);
|
||||
|
||||
/* Ignore SIGPIPE - "broken pipe" / disconnected socket */
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
}
|
||||
|
||||
int client_set_callback(client_t* psClnt, cb_type eTyp, void* funcptr)
|
||||
{
|
||||
require(psClnt != 0);
|
||||
require(funcptr != 0);
|
||||
|
||||
int success = 1;
|
||||
|
||||
switch(eTyp)
|
||||
{
|
||||
case CB_ON_CONNECTION: psClnt->client_connected = funcptr; break;
|
||||
case CB_ON_DISCONNECT: psClnt->client_disconnected = funcptr; break;
|
||||
case CB_RECEIVED_DATA: psClnt->client_new_data = funcptr; break;
|
||||
default: success = 0; /* unknown callback-type */ break;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
int client_send(client_t* psClnt, char* data, uint32_t nbytes)
|
||||
{
|
||||
require(psClnt != 0);
|
||||
require(data != 0);
|
||||
|
||||
psClnt->last_active = time(0);
|
||||
// printf("MQTT: sending %u bytes.\n", nbytes);
|
||||
|
||||
int success = send(psClnt->sockfd, data, nbytes, 0);
|
||||
|
||||
if (success < 0)
|
||||
{
|
||||
perror("send");
|
||||
client_disconnect(psClnt);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
int client_recv(client_t* psClnt, uint32_t timeout_us)
|
||||
{
|
||||
require(psClnt != 0);
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
while (timeout_us >= 1000000)
|
||||
{
|
||||
timeout_us -= 1000000;
|
||||
tv.tv_sec += 1;
|
||||
}
|
||||
tv.tv_usec = timeout_us; /* Not init'ing this can cause strange errors */
|
||||
setsockopt(psClnt->sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv,sizeof(struct timeval));
|
||||
|
||||
int nbytes = recv(psClnt->sockfd, psClnt->rxbuf, psClnt->rxbufsz, 0);
|
||||
if (nbytes <= 0)
|
||||
{
|
||||
/* got error or connection closed by server? */
|
||||
if ( (nbytes == -1) /* nothing for us */
|
||||
|| (nbytes == EAGAIN) /* try again later */
|
||||
|| (nbytes == EWOULDBLOCK)) /* same as above */
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* connection lost / reset by peer */
|
||||
perror("recv");
|
||||
client_disconnect(psClnt);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
psClnt->client_new_data(psClnt, psClnt->rxbuf, nbytes);
|
||||
psClnt->last_active = time(0);
|
||||
}
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
int client_state(client_t* psClnt)
|
||||
{
|
||||
return ((psClnt != 0) ? psClnt->state : 0);
|
||||
}
|
||||
|
||||
void client_poll(client_t* psClnt, uint32_t timeout_us)
|
||||
{
|
||||
/* 10usec timeout is a reasonable minimum I think */
|
||||
timeout_us = ((timeout_us >= 10) ? timeout_us : 10);
|
||||
|
||||
require(psClnt != 0);
|
||||
|
||||
switch (psClnt->state)
|
||||
{
|
||||
case CREATED:
|
||||
{
|
||||
_change_state(psClnt, DISCONNECTED);
|
||||
} break;
|
||||
|
||||
case CONNECTED:
|
||||
{
|
||||
client_recv(psClnt, timeout_us);
|
||||
/*
|
||||
static time_t timeLastMsg = 0;
|
||||
if ((time(0) - timeLastMsg) > 0)
|
||||
{
|
||||
timeLastMsg = time(0);
|
||||
client_send(psClnt, "hej mor!", 8);
|
||||
}
|
||||
*/
|
||||
} break;
|
||||
|
||||
case DISCONNECTED:
|
||||
{
|
||||
/* ~1 second cooldown */
|
||||
if (time(0) > psClnt->last_active)
|
||||
{
|
||||
client_connect(psClnt);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void client_disconnect(client_t* psClnt)
|
||||
{
|
||||
require(psClnt != 0);
|
||||
|
||||
shutdown(psClnt->sockfd, 2);
|
||||
close(psClnt->sockfd);
|
||||
|
||||
_change_state(psClnt, DISCONNECTED);
|
||||
psClnt->client_disconnected(psClnt);
|
||||
}
|
||||
|
||||
int client_connect(client_t* psClnt)
|
||||
{
|
||||
require(psClnt != 0);
|
||||
|
||||
int success = 0;
|
||||
|
||||
struct sockaddr_in serv_addr;
|
||||
struct hostent* server;
|
||||
|
||||
psClnt->sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (psClnt->sockfd < 0)
|
||||
{
|
||||
perror("socket");
|
||||
}
|
||||
else
|
||||
{
|
||||
server = gethostbyname(psClnt->addr);
|
||||
if (server == NULL)
|
||||
{
|
||||
fprintf(stderr,"ERROR, no such host\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(&serv_addr, 0, sizeof(serv_addr));
|
||||
serv_addr.sin_family = AF_INET;
|
||||
memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, server->h_length);
|
||||
serv_addr.sin_port = htons(psClnt->port);
|
||||
|
||||
if (connect(psClnt->sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
|
||||
{
|
||||
perror("connect");
|
||||
/* Clean up by calling close() on socket before allocating a new socket. */
|
||||
client_disconnect(psClnt);
|
||||
}
|
||||
else
|
||||
{
|
||||
_change_state(psClnt, CONNECTED);
|
||||
psClnt->client_connected(psClnt);
|
||||
success = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
54
client.h
Normal file
54
client.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#define NCONNECTIONS 1
|
||||
#define BUFFER_SIZE_BYTES 8000
|
||||
|
||||
/* Assertion macro */
|
||||
#define require(predicate) assert((predicate))
|
||||
|
||||
|
||||
/* Socket connection state */
|
||||
typedef enum
|
||||
{
|
||||
CREATED,
|
||||
CONNECTED,
|
||||
DISCONNECTED,
|
||||
} conn_state_t;
|
||||
|
||||
/* Type definitions */
|
||||
typedef enum
|
||||
{
|
||||
CB_ON_CONNECTION,
|
||||
CB_ON_DISCONNECT,
|
||||
CB_RECEIVED_DATA,
|
||||
} cb_type;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int sockfd;
|
||||
char* rxbuf;
|
||||
uint32_t rxbufsz;
|
||||
conn_state_t state;
|
||||
uint16_t port;
|
||||
char addr[32];
|
||||
time_t last_active;
|
||||
|
||||
/* Callbacks */
|
||||
void (*client_connected) (void* psClnt); /* new connection established */
|
||||
void (*client_disconnected)(void* psClnt); /* a connection was closed */
|
||||
void (*client_new_data) (void* psClnt, char* data, int nbytes); /* data has been received */
|
||||
} client_t;
|
||||
|
||||
|
||||
|
||||
void client_init(client_t* psClnt, char* dst_addr, uint16_t dst_port, char* rxbuf, uint32_t rxbufsize);
|
||||
int client_set_callback(client_t* psClnt, cb_type eTyp, void* funcptr);
|
||||
int client_send(client_t* psClnt, char* data, uint32_t nbytes);
|
||||
int client_recv(client_t* psClnt, uint32_t timeout_us);
|
||||
void client_poll(client_t* psClnt, uint32_t timeout_us);
|
||||
void client_disconnect(client_t* psClnt);
|
||||
int client_connect(client_t* psClnt);
|
||||
int client_state(client_t* psClnt);
|
||||
|
||||
|
||||
133
config.h
Normal file
133
config.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/* config.h. Generated from config.h.in by configure. */
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Directory for the Android daemon storage files */
|
||||
#define ANDROID_STORAGEDIR "/var/lib/bluetooth/android"
|
||||
|
||||
/* Directory for the configuration files */
|
||||
#define CONFIGDIR "/usr/local/etc/bluetooth"
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#define HAVE_DLFCN_H 1
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the <linux/if_alg.h> header file. */
|
||||
#define HAVE_LINUX_IF_ALG_H 1
|
||||
|
||||
/* Define to 1 if you have the <linux/types.h> header file. */
|
||||
#define HAVE_LINUX_TYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#define HAVE_MEMORY_H 1
|
||||
|
||||
/* Define to 1 if you have the <readline/readline.h> header file. */
|
||||
#define HAVE_READLINE_READLINE_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#define HAVE_STRINGS_H 1
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the udev_hwdb_new() function. */
|
||||
#define HAVE_UDEV_HWDB_NEW 1
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||
*/
|
||||
#define LT_OBJDIR ".libs/"
|
||||
|
||||
/* Define if threading support is required */
|
||||
/* #undef NEED_THREADS */
|
||||
|
||||
/* Name of package */
|
||||
#define PACKAGE "bluez"
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT ""
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME "bluez"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "bluez 5.31"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "bluez"
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#define PACKAGE_URL ""
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "5.31"
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Directory for the storage files */
|
||||
#define STORAGEDIR "/var/lib/bluetooth"
|
||||
|
||||
/* Enable extensions on AIX 3, Interix. */
|
||||
#ifndef _ALL_SOURCE
|
||||
# define _ALL_SOURCE 1
|
||||
#endif
|
||||
/* Enable GNU extensions on systems that have them. */
|
||||
#ifndef _GNU_SOURCE
|
||||
# define _GNU_SOURCE 1
|
||||
#endif
|
||||
/* Enable threading extensions on Solaris. */
|
||||
#ifndef _POSIX_PTHREAD_SEMANTICS
|
||||
# define _POSIX_PTHREAD_SEMANTICS 1
|
||||
#endif
|
||||
/* Enable extensions on HP NonStop. */
|
||||
#ifndef _TANDEM_SOURCE
|
||||
# define _TANDEM_SOURCE 1
|
||||
#endif
|
||||
/* Enable general extensions on Solaris. */
|
||||
#ifndef __EXTENSIONS__
|
||||
# define __EXTENSIONS__ 1
|
||||
#endif
|
||||
|
||||
|
||||
/* Version number of package */
|
||||
#define VERSION "5.31"
|
||||
|
||||
/* Define to 1 if on MINIX. */
|
||||
/* #undef _MINIX */
|
||||
|
||||
/* Define to 2 if the system does not provide POSIX.1 features except with
|
||||
this defined. */
|
||||
/* #undef _POSIX_1_SOURCE */
|
||||
|
||||
/* Define to 1 if you need to in order for `stat' and other things to work. */
|
||||
/* #undef _POSIX_SOURCE */
|
||||
|
||||
/* Define to the equivalent of the C99 'restrict' keyword, or to
|
||||
nothing if this is not supported. Do not define if restrict is
|
||||
supported directly. */
|
||||
#define restrict __restrict
|
||||
/* Work around a bug in Sun C++: it does not support _Restrict or
|
||||
__restrict__, even though the corresponding Sun C compiler ends up with
|
||||
"#define restrict _Restrict" or "#define restrict __restrict__" in the
|
||||
previous line. Perhaps some future version of Sun C++ will work with
|
||||
restrict; if so, hopefully it defines __RESTRICT like Sun C does. */
|
||||
#if defined __SUNPRO_CC && !defined __RESTRICT
|
||||
# define _Restrict
|
||||
# define __restrict__
|
||||
#endif
|
||||
701
crypto.c
Normal file
701
crypto.c
Normal file
@@ -0,0 +1,701 @@
|
||||
/**
|
||||
* @file crypto.c
|
||||
* @brief crypting gatt messages
|
||||
* @author Gilbert Brault
|
||||
* @copyright Gilbert Brault 2015
|
||||
* the original work comes from bluez v5.39
|
||||
* value add: documenting main features
|
||||
*
|
||||
*/
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2012-2014 Intel Corporation. All rights reserved.
|
||||
*
|
||||
*
|
||||
* This library 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 library 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 this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "crypto.h"
|
||||
|
||||
#ifndef HAVE_LINUX_IF_ALG_H
|
||||
#ifndef HAVE_LINUX_TYPES_H
|
||||
typedef uint8_t __u8;
|
||||
typedef uint16_t __u16;
|
||||
typedef uint32_t __u32;
|
||||
#else
|
||||
#include <linux/types.h>
|
||||
#endif
|
||||
|
||||
struct sockaddr_alg {
|
||||
__u16 salg_family;
|
||||
__u8 salg_type[14];
|
||||
__u32 salg_feat;
|
||||
__u32 salg_mask;
|
||||
__u8 salg_name[64];
|
||||
};
|
||||
|
||||
struct af_alg_iv {
|
||||
__u32 ivlen;
|
||||
__u8 iv[0];
|
||||
};
|
||||
|
||||
#define ALG_SET_KEY 1
|
||||
#define ALG_SET_IV 2
|
||||
#define ALG_SET_OP 3
|
||||
|
||||
#define ALG_OP_DECRYPT 0
|
||||
#define ALG_OP_ENCRYPT 1
|
||||
|
||||
#define PF_ALG 38 /* Algorithm sockets. */
|
||||
#define AF_ALG PF_ALG
|
||||
#else
|
||||
#include <linux/if_alg.h>
|
||||
#endif
|
||||
|
||||
#ifndef SOL_ALG
|
||||
#define SOL_ALG 279
|
||||
#endif
|
||||
|
||||
/* Maximum message length that can be passed to aes_cmac */
|
||||
#define CMAC_MSG_MAX 80
|
||||
|
||||
struct bt_crypto {
|
||||
int ref_count;
|
||||
int ecb_aes;
|
||||
int urandom;
|
||||
int cmac_aes;
|
||||
};
|
||||
|
||||
/**
|
||||
* open the pseudo random os generator, returns the associated file descriptor
|
||||
*
|
||||
* @return pseudo-random generator associated file descriptor
|
||||
*/
|
||||
static int urandom_setup(void)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static int ecb_aes_setup(void)
|
||||
{
|
||||
struct sockaddr_alg salg;
|
||||
int fd;
|
||||
|
||||
fd = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
memset(&salg, 0, sizeof(salg));
|
||||
salg.salg_family = AF_ALG;
|
||||
strcpy((char *) salg.salg_type, "skcipher");
|
||||
strcpy((char *) salg.salg_name, "ecb(aes)");
|
||||
|
||||
if (bind(fd, (struct sockaddr *) &salg, sizeof(salg)) < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int cmac_aes_setup(void)
|
||||
{
|
||||
struct sockaddr_alg salg;
|
||||
int fd;
|
||||
|
||||
fd = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
memset(&salg, 0, sizeof(salg));
|
||||
salg.salg_family = AF_ALG;
|
||||
strcpy((char *) salg.salg_type, "hash");
|
||||
strcpy((char *) salg.salg_name, "cmac(aes)");
|
||||
|
||||
if (bind(fd, (struct sockaddr *) &salg, sizeof(salg)) < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
struct bt_crypto *bt_crypto_new(void)
|
||||
{
|
||||
struct bt_crypto *crypto;
|
||||
|
||||
crypto = new0(struct bt_crypto, 1);
|
||||
if (!crypto)
|
||||
return NULL;
|
||||
|
||||
crypto->ecb_aes = ecb_aes_setup();
|
||||
if (crypto->ecb_aes < 0) {
|
||||
free(crypto);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
crypto->urandom = urandom_setup();
|
||||
if (crypto->urandom < 0) {
|
||||
close(crypto->ecb_aes);
|
||||
free(crypto);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
crypto->cmac_aes = cmac_aes_setup();
|
||||
if (crypto->cmac_aes < 0) {
|
||||
close(crypto->urandom);
|
||||
close(crypto->ecb_aes);
|
||||
free(crypto);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return bt_crypto_ref(crypto);
|
||||
}
|
||||
|
||||
struct bt_crypto *bt_crypto_ref(struct bt_crypto *crypto)
|
||||
{
|
||||
if (!crypto)
|
||||
return NULL;
|
||||
|
||||
__sync_fetch_and_add(&crypto->ref_count, 1);
|
||||
|
||||
return crypto;
|
||||
}
|
||||
|
||||
void bt_crypto_unref(struct bt_crypto *crypto)
|
||||
{
|
||||
if (!crypto)
|
||||
return;
|
||||
|
||||
if (__sync_sub_and_fetch(&crypto->ref_count, 1))
|
||||
return;
|
||||
|
||||
close(crypto->urandom);
|
||||
close(crypto->ecb_aes);
|
||||
close(crypto->cmac_aes);
|
||||
|
||||
free(crypto);
|
||||
}
|
||||
|
||||
bool bt_crypto_random_bytes(struct bt_crypto *crypto,
|
||||
uint8_t *buf, uint8_t num_bytes)
|
||||
{
|
||||
ssize_t len;
|
||||
|
||||
if (!crypto)
|
||||
return false;
|
||||
|
||||
len = read(crypto->urandom, buf, num_bytes);
|
||||
if (len < num_bytes)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int alg_new(int fd, const void *keyval, socklen_t keylen)
|
||||
{
|
||||
if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, keyval, keylen) < 0)
|
||||
return -1;
|
||||
|
||||
/* FIXME: This should use accept4() with SOCK_CLOEXEC */
|
||||
return accept(fd, NULL, 0);
|
||||
}
|
||||
|
||||
static bool alg_encrypt(int fd, const void *inbuf, size_t inlen,
|
||||
void *outbuf, size_t outlen)
|
||||
{
|
||||
__u32 alg_op = ALG_OP_ENCRYPT;
|
||||
char cbuf[CMSG_SPACE(sizeof(alg_op))];
|
||||
struct cmsghdr *cmsg;
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
ssize_t len;
|
||||
|
||||
memset(cbuf, 0, sizeof(cbuf));
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
msg.msg_control = cbuf;
|
||||
msg.msg_controllen = sizeof(cbuf);
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_level = SOL_ALG;
|
||||
cmsg->cmsg_type = ALG_SET_OP;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(alg_op));
|
||||
memcpy(CMSG_DATA(cmsg), &alg_op, sizeof(alg_op));
|
||||
|
||||
iov.iov_base = (void *) inbuf;
|
||||
iov.iov_len = inlen;
|
||||
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
len = sendmsg(fd, &msg, 0);
|
||||
if (len < 0)
|
||||
return false;
|
||||
|
||||
len = read(fd, outbuf, outlen);
|
||||
if (len < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void swap_buf(const uint8_t *src, uint8_t *dst, uint16_t len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
dst[len - 1 - i] = src[i];
|
||||
}
|
||||
|
||||
bool bt_crypto_sign_att(struct bt_crypto *crypto, const uint8_t key[16],
|
||||
const uint8_t *m, uint16_t m_len,
|
||||
uint32_t sign_cnt, uint8_t signature[12])
|
||||
{
|
||||
int fd;
|
||||
int len;
|
||||
uint8_t tmp[16], out[16];
|
||||
uint16_t msg_len = m_len + sizeof(uint32_t);
|
||||
uint8_t msg[msg_len];
|
||||
uint8_t msg_s[msg_len];
|
||||
|
||||
if (!crypto)
|
||||
return false;
|
||||
|
||||
memset(msg, 0, msg_len);
|
||||
memcpy(msg, m, m_len);
|
||||
|
||||
/* Add sign_counter to the message */
|
||||
put_le32(sign_cnt, msg + m_len);
|
||||
|
||||
/* The most significant octet of key corresponds to key[0] */
|
||||
swap_buf(key, tmp, 16);
|
||||
|
||||
fd = alg_new(crypto->cmac_aes, tmp, 16);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
|
||||
/* Swap msg before signing */
|
||||
swap_buf(msg, msg_s, msg_len);
|
||||
|
||||
len = send(fd, msg_s, msg_len, 0);
|
||||
if (len < 0) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
len = read(fd, out, 16);
|
||||
if (len < 0) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
/*
|
||||
* As to BT spec. 4.1 Vol[3], Part C, chapter 10.4.1 sign counter should
|
||||
* be placed in the signature
|
||||
*/
|
||||
put_be32(sign_cnt, out + 8);
|
||||
|
||||
/*
|
||||
* The most significant octet of hash corresponds to out[0] - swap it.
|
||||
* Then truncate in most significant bit first order to a length of
|
||||
* 12 octets
|
||||
*/
|
||||
swap_buf(out, tmp, 16);
|
||||
memcpy(signature, tmp + 4, 12);
|
||||
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Security function e
|
||||
*
|
||||
* Security function e generates 128-bit encryptedData from a 128-bit key
|
||||
* and 128-bit plaintextData using the AES-128-bit block cypher:
|
||||
*
|
||||
* encryptedData = e(key, plaintextData)
|
||||
*
|
||||
* The most significant octet of key corresponds to key[0], the most
|
||||
* significant octet of plaintextData corresponds to in[0] and the
|
||||
* most significant octet of encryptedData corresponds to out[0].
|
||||
*
|
||||
*/
|
||||
bool bt_crypto_e(struct bt_crypto *crypto, const uint8_t key[16],
|
||||
const uint8_t plaintext[16], uint8_t encrypted[16])
|
||||
{
|
||||
uint8_t tmp[16], in[16], out[16];
|
||||
int fd;
|
||||
|
||||
if (!crypto)
|
||||
return false;
|
||||
|
||||
/* The most significant octet of key corresponds to key[0] */
|
||||
swap_buf(key, tmp, 16);
|
||||
|
||||
fd = alg_new(crypto->ecb_aes, tmp, 16);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
|
||||
|
||||
/* Most significant octet of plaintextData corresponds to in[0] */
|
||||
swap_buf(plaintext, in, 16);
|
||||
|
||||
if (!alg_encrypt(fd, in, 16, out, 16)) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Most significant octet of encryptedData corresponds to out[0] */
|
||||
swap_buf(out, encrypted, 16);
|
||||
|
||||
close(fd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Random Address Hash function ah
|
||||
*
|
||||
* The random address hash function ah is used to generate a hash value
|
||||
* that is used in resolvable private addresses.
|
||||
*
|
||||
* The following are inputs to the random address hash function ah:
|
||||
*
|
||||
* k is 128 bits
|
||||
* r is 24 bits
|
||||
* padding is 104 bits
|
||||
*
|
||||
* r is concatenated with padding to generate r' which is used as the
|
||||
* 128-bit input parameter plaintextData to security function e:
|
||||
*
|
||||
* r' = padding || r
|
||||
*
|
||||
* The least significant octet of r becomes the least significant octet
|
||||
* of r’ and the most significant octet of padding becomes the most
|
||||
* significant octet of r'.
|
||||
*
|
||||
* For example, if the 24-bit value r is 0x423456 then r' is
|
||||
* 0x00000000000000000000000000423456.
|
||||
*
|
||||
* The output of the random address function ah is:
|
||||
*
|
||||
* ah(k, r) = e(k, r') mod 2^24
|
||||
*
|
||||
* The output of the security function e is then truncated to 24 bits by
|
||||
* taking the least significant 24 bits of the output of e as the result
|
||||
* of ah.
|
||||
*/
|
||||
bool bt_crypto_ah(struct bt_crypto *crypto, const uint8_t k[16],
|
||||
const uint8_t r[3], uint8_t hash[3])
|
||||
{
|
||||
uint8_t rp[16];
|
||||
uint8_t encrypted[16];
|
||||
|
||||
if (!crypto)
|
||||
return false;
|
||||
|
||||
/* r' = padding || r */
|
||||
memcpy(rp, r, 3);
|
||||
memset(rp + 3, 0, 13);
|
||||
|
||||
/* e(k, r') */
|
||||
if (!bt_crypto_e(crypto, k, rp, encrypted))
|
||||
return false;
|
||||
|
||||
/* ah(k, r) = e(k, r') mod 2^24 */
|
||||
memcpy(hash, encrypted, 3);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint64_t a, b;
|
||||
} u128;
|
||||
|
||||
static inline void u128_xor(const uint8_t p[16], const uint8_t q[16],
|
||||
uint8_t r[16])
|
||||
{
|
||||
u128 pp, qq, rr;
|
||||
|
||||
memcpy(&pp, p, 16);
|
||||
memcpy(&qq, q, 16);
|
||||
|
||||
rr.a = pp.a ^ qq.a;
|
||||
rr.b = pp.b ^ qq.b;
|
||||
|
||||
memcpy(r, &rr, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm value generation function c1
|
||||
*
|
||||
* During the pairing process confirm values are exchanged. This confirm
|
||||
* value generation function c1 is used to generate the confirm values.
|
||||
*
|
||||
* The following are inputs to the confirm value generation function c1:
|
||||
*
|
||||
* k is 128 bits
|
||||
* r is 128 bits
|
||||
* pres is 56 bits
|
||||
* preq is 56 bits
|
||||
* iat is 1 bit
|
||||
* ia is 48 bits
|
||||
* rat is 1 bit
|
||||
* ra is 48 bits
|
||||
* padding is 32 bits of 0
|
||||
*
|
||||
* iat is concatenated with 7-bits of 0 to create iat' which is 8 bits
|
||||
* in length. iat is the least significant bit of iat'
|
||||
*
|
||||
* rat is concatenated with 7-bits of 0 to create rat' which is 8 bits
|
||||
* in length. rat is the least significant bit of rat'
|
||||
*
|
||||
* pres, preq, rat' and iat' are concatenated to generate p1 which is
|
||||
* XORed with r and used as 128-bit input parameter plaintextData to
|
||||
* security function e:
|
||||
*
|
||||
* p1 = pres || preq || rat' || iat'
|
||||
*
|
||||
* The octet of iat' becomes the least significant octet of p1 and the
|
||||
* most significant octet of pres becomes the most significant octet of
|
||||
* p1.
|
||||
*
|
||||
* ra is concatenated with ia and padding to generate p2 which is XORed
|
||||
* with the result of the security function e using p1 as the input
|
||||
* paremter plaintextData and is then used as the 128-bit input
|
||||
* parameter plaintextData to security function e:
|
||||
*
|
||||
* p2 = padding || ia || ra
|
||||
*
|
||||
* The least significant octet of ra becomes the least significant octet
|
||||
* of p2 and the most significant octet of padding becomes the most
|
||||
* significant octet of p2.
|
||||
*
|
||||
* The output of the confirm value generation function c1 is:
|
||||
*
|
||||
* c1(k, r, preq, pres, iat, rat, ia, ra) = e(k, e(k, r XOR p1) XOR p2)
|
||||
*
|
||||
* The 128-bit output of the security function e is used as the result
|
||||
* of confirm value generation function c1.
|
||||
*/
|
||||
bool bt_crypto_c1(struct bt_crypto *crypto, const uint8_t k[16],
|
||||
const uint8_t r[16], const uint8_t pres[7],
|
||||
const uint8_t preq[7], uint8_t iat,
|
||||
const uint8_t ia[6], uint8_t rat,
|
||||
const uint8_t ra[6], uint8_t res[16])
|
||||
{
|
||||
uint8_t p1[16], p2[16];
|
||||
|
||||
/* p1 = pres || preq || _rat || _iat */
|
||||
p1[0] = iat;
|
||||
p1[1] = rat;
|
||||
memcpy(p1 + 2, preq, 7);
|
||||
memcpy(p1 + 9, pres, 7);
|
||||
|
||||
/* p2 = padding || ia || ra */
|
||||
memcpy(p2, ra, 6);
|
||||
memcpy(p2 + 6, ia, 6);
|
||||
memset(p2 + 12, 0, 4);
|
||||
|
||||
/* res = r XOR p1 */
|
||||
u128_xor(r, p1, res);
|
||||
|
||||
/* res = e(k, res) */
|
||||
if (!bt_crypto_e(crypto, k, res, res))
|
||||
return false;
|
||||
|
||||
/* res = res XOR p2 */
|
||||
u128_xor(res, p2, res);
|
||||
|
||||
/* res = e(k, res) */
|
||||
return bt_crypto_e(crypto, k, res, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Key generation function s1
|
||||
*
|
||||
* The key generation function s1 is used to generate the STK during the
|
||||
* pairing process.
|
||||
*
|
||||
* The following are inputs to the key generation function s1:
|
||||
*
|
||||
* k is 128 bits
|
||||
* r1 is 128 bits
|
||||
* r2 is 128 bits
|
||||
*
|
||||
* The most significant 64-bits of r1 are discarded to generate r1' and
|
||||
* the most significant 64-bits of r2 are discarded to generate r2'.
|
||||
*
|
||||
* r1' is concatenated with r2' to generate r' which is used as the
|
||||
* 128-bit input parameter plaintextData to security function e:
|
||||
*
|
||||
* r' = r1' || r2'
|
||||
*
|
||||
* The least significant octet of r2' becomes the least significant
|
||||
* octet of r' and the most significant octet of r1' becomes the most
|
||||
* significant octet of r'.
|
||||
*
|
||||
* The output of the key generation function s1 is:
|
||||
*
|
||||
* s1(k, r1, r2) = e(k, r')
|
||||
*
|
||||
* The 128-bit output of the security function e is used as the result
|
||||
* of key generation function s1.
|
||||
*/
|
||||
bool bt_crypto_s1(struct bt_crypto *crypto, const uint8_t k[16],
|
||||
const uint8_t r1[16], const uint8_t r2[16],
|
||||
uint8_t res[16])
|
||||
{
|
||||
memcpy(res, r2, 8);
|
||||
memcpy(res + 8, r1, 8);
|
||||
|
||||
return bt_crypto_e(crypto, k, res, res);
|
||||
}
|
||||
|
||||
static bool aes_cmac(struct bt_crypto *crypto, uint8_t key[16], uint8_t *msg,
|
||||
size_t msg_len, uint8_t res[16])
|
||||
{
|
||||
uint8_t key_msb[16], out[16], msg_msb[CMAC_MSG_MAX];
|
||||
ssize_t len;
|
||||
int fd;
|
||||
|
||||
if (msg_len > CMAC_MSG_MAX)
|
||||
return false;
|
||||
|
||||
swap_buf(key, key_msb, 16);
|
||||
fd = alg_new(crypto->cmac_aes, key_msb, 16);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
|
||||
swap_buf(msg, msg_msb, msg_len);
|
||||
len = send(fd, msg_msb, msg_len, 0);
|
||||
if (len < 0) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
len = read(fd, out, 16);
|
||||
if (len < 0) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
swap_buf(out, res, 16);
|
||||
|
||||
close(fd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bt_crypto_f4(struct bt_crypto *crypto, uint8_t u[32], uint8_t v[32],
|
||||
uint8_t x[16], uint8_t z, uint8_t res[16])
|
||||
{
|
||||
uint8_t m[65];
|
||||
|
||||
if (!crypto)
|
||||
return false;
|
||||
|
||||
m[0] = z;
|
||||
memcpy(&m[1], v, 32);
|
||||
memcpy(&m[33], u, 32);
|
||||
|
||||
return aes_cmac(crypto, x, m, sizeof(m), res);
|
||||
}
|
||||
|
||||
bool bt_crypto_f5(struct bt_crypto *crypto, uint8_t w[32], uint8_t n1[16],
|
||||
uint8_t n2[16], uint8_t a1[7], uint8_t a2[7],
|
||||
uint8_t mackey[16], uint8_t ltk[16])
|
||||
{
|
||||
uint8_t btle[4] = { 0x65, 0x6c, 0x74, 0x62 };
|
||||
uint8_t salt[16] = { 0xbe, 0x83, 0x60, 0x5a, 0xdb, 0x0b, 0x37, 0x60,
|
||||
0x38, 0xa5, 0xf5, 0xaa, 0x91, 0x83, 0x88, 0x6c };
|
||||
uint8_t length[2] = { 0x00, 0x01 };
|
||||
uint8_t m[53], t[16];
|
||||
|
||||
if (!aes_cmac(crypto, salt, w, 32, t))
|
||||
return false;
|
||||
|
||||
memcpy(&m[0], length, 2);
|
||||
memcpy(&m[2], a2, 7);
|
||||
memcpy(&m[9], a1, 7);
|
||||
memcpy(&m[16], n2, 16);
|
||||
memcpy(&m[32], n1, 16);
|
||||
memcpy(&m[48], btle, 4);
|
||||
|
||||
m[52] = 0; /* Counter */
|
||||
if (!aes_cmac(crypto, t, m, sizeof(m), mackey))
|
||||
return false;
|
||||
|
||||
m[52] = 1; /* Counter */
|
||||
return aes_cmac(crypto, t, m, sizeof(m), ltk);
|
||||
}
|
||||
|
||||
bool bt_crypto_f6(struct bt_crypto *crypto, uint8_t w[16], uint8_t n1[16],
|
||||
uint8_t n2[16], uint8_t r[16], uint8_t io_cap[3],
|
||||
uint8_t a1[7], uint8_t a2[7], uint8_t res[16])
|
||||
{
|
||||
uint8_t m[65];
|
||||
|
||||
memcpy(&m[0], a2, 7);
|
||||
memcpy(&m[7], a1, 7);
|
||||
memcpy(&m[14], io_cap, 3);
|
||||
memcpy(&m[17], r, 16);
|
||||
memcpy(&m[33], n2, 16);
|
||||
memcpy(&m[49], n1, 16);
|
||||
|
||||
return aes_cmac(crypto, w, m, sizeof(m), res);
|
||||
}
|
||||
|
||||
bool bt_crypto_g2(struct bt_crypto *crypto, uint8_t u[32], uint8_t v[32],
|
||||
uint8_t x[16], uint8_t y[16], uint32_t *val)
|
||||
{
|
||||
uint8_t m[80], tmp[16];
|
||||
|
||||
memcpy(&m[0], y, 16);
|
||||
memcpy(&m[16], v, 32);
|
||||
memcpy(&m[48], u, 32);
|
||||
|
||||
if (!aes_cmac(crypto, x, m, sizeof(m), tmp))
|
||||
return false;
|
||||
|
||||
*val = get_le32(tmp);
|
||||
*val %= 1000000;
|
||||
|
||||
return true;
|
||||
}
|
||||
61
crypto.h
Normal file
61
crypto.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2012-2014 Intel Corporation. All rights reserved.
|
||||
*
|
||||
*
|
||||
* This library 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 library 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 this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct bt_crypto;
|
||||
|
||||
struct bt_crypto *bt_crypto_new(void);
|
||||
|
||||
struct bt_crypto *bt_crypto_ref(struct bt_crypto *crypto);
|
||||
void bt_crypto_unref(struct bt_crypto *crypto);
|
||||
|
||||
bool bt_crypto_random_bytes(struct bt_crypto *crypto,
|
||||
uint8_t *buf, uint8_t num_bytes);
|
||||
|
||||
bool bt_crypto_e(struct bt_crypto *crypto, const uint8_t key[16],
|
||||
const uint8_t plaintext[16], uint8_t encrypted[16]);
|
||||
bool bt_crypto_ah(struct bt_crypto *crypto, const uint8_t k[16],
|
||||
const uint8_t r[3], uint8_t hash[3]);
|
||||
bool bt_crypto_c1(struct bt_crypto *crypto, const uint8_t k[16],
|
||||
const uint8_t r[16], const uint8_t pres[7],
|
||||
const uint8_t preq[7], uint8_t iat,
|
||||
const uint8_t ia[6], uint8_t rat,
|
||||
const uint8_t ra[6], uint8_t res[16]);
|
||||
bool bt_crypto_s1(struct bt_crypto *crypto, const uint8_t k[16],
|
||||
const uint8_t r1[16], const uint8_t r2[16],
|
||||
uint8_t res[16]);
|
||||
bool bt_crypto_f4(struct bt_crypto *crypto, uint8_t u[32], uint8_t v[32],
|
||||
uint8_t x[16], uint8_t z, uint8_t res[16]);
|
||||
bool bt_crypto_f5(struct bt_crypto *crypto, uint8_t w[32], uint8_t n1[16],
|
||||
uint8_t n2[16], uint8_t a1[7], uint8_t a2[7],
|
||||
uint8_t mackey[16], uint8_t ltk[16]);
|
||||
bool bt_crypto_f6(struct bt_crypto *crypto, uint8_t w[16], uint8_t n1[16],
|
||||
uint8_t n2[16], uint8_t r[16], uint8_t io_cap[3],
|
||||
uint8_t a1[7], uint8_t a2[7], uint8_t res[16]);
|
||||
bool bt_crypto_g2(struct bt_crypto *crypto, uint8_t u[32], uint8_t v[32],
|
||||
uint8_t x[16], uint8_t y[16], uint32_t *val);
|
||||
bool bt_crypto_sign_att(struct bt_crypto *crypto, const uint8_t key[16],
|
||||
const uint8_t *m, uint16_t m_len,
|
||||
uint32_t sign_cnt, uint8_t signature[12]);
|
||||
112
design_notes.h
Normal file
112
design_notes.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* @file design_notes.h
|
||||
* @brief notes about btgattclient software design
|
||||
*
|
||||
* We discuss about various topics concerning btgattclient software design and architecture:
|
||||
* - epoll: a technic, specific to Linux, to manage in a single loop various i/o events associated with files (incl. standard i/o) and sockets
|
||||
* - tbc: some more details to come
|
||||
*
|
||||
* @author Gilbert Brault
|
||||
* @copyright Gilbert Brault 2015
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* design_notes.h
|
||||
*
|
||||
* Created on: 30 juil. 2015
|
||||
* Author: gilbert
|
||||
*/
|
||||
|
||||
#ifndef DESIGN_NOTES_H_
|
||||
#define DESIGN_NOTES_H_
|
||||
#include <sys/epoll.h>
|
||||
/**
|
||||
* controls the epoll instance associated with epfd
|
||||
*
|
||||
* @param epfd the epoll context structure
|
||||
* @param op specifies the operation to be taken against the file associated with fd
|
||||
* @param fd file descriptor can be a socket
|
||||
* @param event epool event further constrains the behavior of the operation
|
||||
* @return 0 success <0 error
|
||||
*/
|
||||
int epoll_ctl_doc (int epfd,
|
||||
int op,
|
||||
int fd,
|
||||
struct epoll_event *event);
|
||||
|
||||
/**
|
||||
* @brief epool event structure 4th parameter of epoll_ctl(...)
|
||||
*/
|
||||
struct epoll_event_doc {
|
||||
/// and EPOLL_EVENT value (might be ored)
|
||||
int events;
|
||||
union {
|
||||
void *ptr;
|
||||
int fd;
|
||||
int u32;
|
||||
long u64;
|
||||
} data;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief EPOLL_CTL_XXX possible values
|
||||
*/
|
||||
enum EPOLL_CTL_DOC {
|
||||
/// EPOLL_CTL_ADD: Add a monitor on the file associated with the file descriptor fd to the epoll instance associated with epfd, per the events defined in event.
|
||||
EPOLL_CTL_ADD_DOC=EPOLL_CTL_ADD,
|
||||
/// EPOLL_CTL_DEL: Remove a monitor on the file associated with the file descriptor fd from the epoll instance associated with epfd.
|
||||
EPOLL_CTL_DEL_DOC=EPOLL_CTL_DEL,
|
||||
/// EPOLL_CTL_MOD: Modify an existing monitor of fd with the updated events specified by event.
|
||||
EPOLL_CTL_MOD_DOC=EPOLL_CTL_MOD
|
||||
};
|
||||
|
||||
/** @brief EPOLL_EVENTS possible values
|
||||
*
|
||||
<code>
|
||||
The events field in the epoll_event structure lists which events to monitor on the given file descriptor.
|
||||
Multiple events can be bitwise-ORed together.
|
||||
|
||||
Edge- Versus Level-Triggered Events
|
||||
|
||||
If the EPOLLET value is set in the events field of the event parameter passed to epoll_ctl( ),
|
||||
the watch on fd is edge-triggered, as opposed to level-triggered.
|
||||
|
||||
Consider the following events between a producer and a consumer communicating over a Unix pipe:
|
||||
|
||||
The producer writes 1 KB of data onto a pipe.
|
||||
|
||||
The consumer performs an epoll_wait( ) on the pipe, waiting for the pipe to contain data, and thus be readable.
|
||||
|
||||
With a level-triggered watch, the call to epoll_wait( ) in step 2 will return immediately,
|
||||
showing that the pipe is ready to read.
|
||||
|
||||
With an edge-triggered watch,this call will not return until after step 1 occurs.
|
||||
That is, even if the pipe is readable at the invocation of epoll_wait( ),
|
||||
the call will not return until the data is written onto the pipe.
|
||||
|
||||
Level-triggered is the default behavior. It is how poll( ) and select( ) behave,
|
||||
and it is what most developers expect. Edge-triggered behavior requires a different approach
|
||||
to programming, commonly utilizing nonblocking I/O, and careful checking for EAGAIN.
|
||||
</code>
|
||||
|
||||
*/
|
||||
enum EPOLL_EVENTS_DOC {
|
||||
/// EPOLLERR: An error condition occurred on the file. This event is always monitored, even if it's not specified.
|
||||
EPOLLERR_DOC=EPOLLERR,
|
||||
/// EPOLLET: Enables edge-triggered behavior for the monitor of the file (see the upcoming section "Edge- Versus Level-Triggered Events"). The default behavior is level-triggered.
|
||||
EPOLLET_DOC=EPOLLET,
|
||||
/// EPOLLHUP: A hangup occurred on the file. This event is always monitored, even if it's not specified.
|
||||
EPOLLHUP_DOC=EPOLLHUP,
|
||||
/// EPOLLIN: The file is available to be read from without blocking
|
||||
EPOLLIN_DOC=EPOLLIN,
|
||||
/// EPOLLONESHOT: After an event is generated and read, the file is automatically no longer monitored. A new event mask must be specified via EPOLL_CTL_MOD to reenable the watch.
|
||||
EPOLLONESHOT_DOC=EPOLLONESHOT,
|
||||
/// EPOLLOUT: The file is available to be written to without blocking.
|
||||
EPOLLOUT_DOC=EPOLLOUT,
|
||||
/// EPOLLPRI: There is urgent out-of-band data available to read.
|
||||
EPOLLPRI_DOC=EPOLLPRI
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* DESIGN_NOTES_H_ */
|
||||
3069
gatt-client.c
Normal file
3069
gatt-client.c
Normal file
File diff suppressed because it is too large
Load Diff
135
gatt-client.h
Normal file
135
gatt-client.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2014 Google Inc.
|
||||
*
|
||||
*
|
||||
* This library 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 library 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 this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define BT_GATT_UUID_SIZE 16
|
||||
|
||||
struct bt_gatt_client;
|
||||
|
||||
struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db,
|
||||
struct bt_att *att,
|
||||
uint16_t mtu);
|
||||
|
||||
struct bt_gatt_client *bt_gatt_client_ref(struct bt_gatt_client *client);
|
||||
void bt_gatt_client_unref(struct bt_gatt_client *client);
|
||||
|
||||
typedef void (*bt_gatt_client_destroy_func_t)(void *user_data);
|
||||
typedef void (*bt_gatt_client_callback_t)(bool success, uint8_t att_ecode,
|
||||
void *user_data);
|
||||
typedef void (*bt_gatt_client_debug_func_t)(const char *str, void *user_data);
|
||||
typedef void (*bt_gatt_client_read_callback_t)(bool success, uint8_t att_ecode,
|
||||
const uint8_t *value, uint16_t length,
|
||||
void *user_data);
|
||||
typedef void (*bt_gatt_client_write_long_callback_t)(bool success,
|
||||
bool reliable_error, uint8_t att_ecode,
|
||||
void *user_data);
|
||||
typedef void (*bt_gatt_client_notify_callback_t)(uint16_t value_handle,
|
||||
const uint8_t *value, uint16_t length,
|
||||
void *user_data);
|
||||
typedef void (*bt_gatt_client_register_callback_t)(uint16_t att_ecode,
|
||||
void *user_data);
|
||||
typedef void (*bt_gatt_client_service_changed_callback_t)(uint16_t start_handle,
|
||||
uint16_t end_handle,
|
||||
void *user_data);
|
||||
|
||||
bool bt_gatt_client_is_ready(struct bt_gatt_client *client);
|
||||
bool bt_gatt_client_set_ready_handler(struct bt_gatt_client *client,
|
||||
bt_gatt_client_callback_t callback,
|
||||
void *user_data,
|
||||
bt_gatt_client_destroy_func_t destroy);
|
||||
bool bt_gatt_client_set_service_changed(struct bt_gatt_client *client,
|
||||
bt_gatt_client_service_changed_callback_t callback,
|
||||
void *user_data,
|
||||
bt_gatt_client_destroy_func_t destroy);
|
||||
bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
|
||||
bt_gatt_client_debug_func_t callback,
|
||||
void *user_data,
|
||||
bt_gatt_client_destroy_func_t destroy);
|
||||
|
||||
uint16_t bt_gatt_client_get_mtu(struct bt_gatt_client *client);
|
||||
struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client);
|
||||
|
||||
bool bt_gatt_client_cancel(struct bt_gatt_client *client, unsigned int id);
|
||||
bool bt_gatt_client_cancel_all(struct bt_gatt_client *client);
|
||||
|
||||
unsigned int bt_gatt_client_read_value(struct bt_gatt_client *client,
|
||||
uint16_t value_handle,
|
||||
bt_gatt_client_read_callback_t callback,
|
||||
void *user_data,
|
||||
bt_gatt_client_destroy_func_t destroy);
|
||||
unsigned int bt_gatt_client_read_long_value(struct bt_gatt_client *client,
|
||||
uint16_t value_handle, uint16_t offset,
|
||||
bt_gatt_client_read_callback_t callback,
|
||||
void *user_data,
|
||||
bt_gatt_client_destroy_func_t destroy);
|
||||
unsigned int bt_gatt_client_read_multiple(struct bt_gatt_client *client,
|
||||
uint16_t *handles, uint8_t num_handles,
|
||||
bt_gatt_client_read_callback_t callback,
|
||||
void *user_data,
|
||||
bt_gatt_client_destroy_func_t destroy);
|
||||
|
||||
unsigned int bt_gatt_client_write_without_response(
|
||||
struct bt_gatt_client *client,
|
||||
uint16_t value_handle,
|
||||
bool signed_write,
|
||||
const uint8_t *value, uint16_t length);
|
||||
unsigned int bt_gatt_client_write_value(struct bt_gatt_client *client,
|
||||
uint16_t value_handle,
|
||||
const uint8_t *value, uint16_t length,
|
||||
bt_gatt_client_callback_t callback,
|
||||
void *user_data,
|
||||
bt_gatt_client_destroy_func_t destroy);
|
||||
unsigned int bt_gatt_client_write_long_value(struct bt_gatt_client *client,
|
||||
bool reliable,
|
||||
uint16_t value_handle, uint16_t offset,
|
||||
const uint8_t *value, uint16_t length,
|
||||
bt_gatt_client_write_long_callback_t callback,
|
||||
void *user_data,
|
||||
bt_gatt_client_destroy_func_t destroy);
|
||||
unsigned int bt_gatt_client_prepare_write(struct bt_gatt_client *client,
|
||||
unsigned int id,
|
||||
uint16_t value_handle, uint16_t offset,
|
||||
const uint8_t *value, uint16_t length,
|
||||
bt_gatt_client_write_long_callback_t callback,
|
||||
void *user_data,
|
||||
bt_gatt_client_destroy_func_t destroy);
|
||||
unsigned int bt_gatt_client_write_execute(struct bt_gatt_client *client,
|
||||
unsigned int id,
|
||||
bt_gatt_client_callback_t callback,
|
||||
void *user_data,
|
||||
bt_gatt_client_destroy_func_t destroy);
|
||||
|
||||
unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
|
||||
uint16_t chrc_value_handle,
|
||||
bt_gatt_client_register_callback_t callback,
|
||||
bt_gatt_client_notify_callback_t notify,
|
||||
void *user_data,
|
||||
bt_gatt_client_destroy_func_t destroy);
|
||||
bool bt_gatt_client_unregister_notify(struct bt_gatt_client *client,
|
||||
unsigned int id);
|
||||
|
||||
bool bt_gatt_client_set_security(struct bt_gatt_client *client, int level);
|
||||
int bt_gatt_client_get_security(struct bt_gatt_client *client);
|
||||
236
gatt-db.h
Normal file
236
gatt-db.h
Normal file
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2014 Intel Corporation. All rights reserved.
|
||||
*
|
||||
*
|
||||
* This library 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 library 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 this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
struct gatt_db;
|
||||
struct gatt_db_attribute;
|
||||
|
||||
struct gatt_db *gatt_db_new(void);
|
||||
|
||||
struct gatt_db *gatt_db_ref(struct gatt_db *db);
|
||||
void gatt_db_unref(struct gatt_db *db);
|
||||
|
||||
bool gatt_db_isempty(struct gatt_db *db);
|
||||
|
||||
struct gatt_db_attribute *gatt_db_add_service(struct gatt_db *db,
|
||||
const bt_uuid_t *uuid,
|
||||
bool primary,
|
||||
uint16_t num_handles);
|
||||
|
||||
bool gatt_db_remove_service(struct gatt_db *db,
|
||||
struct gatt_db_attribute *attrib);
|
||||
bool gatt_db_clear(struct gatt_db *db);
|
||||
bool gatt_db_clear_range(struct gatt_db *db, uint16_t start_handle,
|
||||
uint16_t end_handle);
|
||||
|
||||
struct gatt_db_attribute *gatt_db_insert_service(struct gatt_db *db,
|
||||
uint16_t handle,
|
||||
const bt_uuid_t *uuid,
|
||||
bool primary,
|
||||
uint16_t num_handles);
|
||||
|
||||
typedef void (*gatt_db_read_t) (struct gatt_db_attribute *attrib,
|
||||
unsigned int id, uint16_t offset,
|
||||
uint8_t opcode, struct bt_att *att,
|
||||
void *user_data);
|
||||
|
||||
typedef void (*gatt_db_write_t) (struct gatt_db_attribute *attrib,
|
||||
unsigned int id, uint16_t offset,
|
||||
const uint8_t *value, size_t len,
|
||||
uint8_t opcode, struct bt_att *att,
|
||||
void *user_data);
|
||||
|
||||
struct gatt_db_attribute *
|
||||
gatt_db_service_add_characteristic(struct gatt_db_attribute *attrib,
|
||||
const bt_uuid_t *uuid,
|
||||
uint32_t permissions,
|
||||
uint8_t properties,
|
||||
gatt_db_read_t read_func,
|
||||
gatt_db_write_t write_func,
|
||||
void *user_data);
|
||||
struct gatt_db_attribute *
|
||||
gatt_db_service_insert_characteristic(struct gatt_db_attribute *attrib,
|
||||
uint16_t handle,
|
||||
const bt_uuid_t *uuid,
|
||||
uint32_t permissions,
|
||||
uint8_t properties,
|
||||
gatt_db_read_t read_func,
|
||||
gatt_db_write_t write_func,
|
||||
void *user_data);
|
||||
|
||||
struct gatt_db_attribute *
|
||||
gatt_db_service_add_descriptor(struct gatt_db_attribute *attrib,
|
||||
const bt_uuid_t *uuid,
|
||||
uint32_t permissions,
|
||||
gatt_db_read_t read_func,
|
||||
gatt_db_write_t write_func,
|
||||
void *user_data);
|
||||
struct gatt_db_attribute *
|
||||
gatt_db_service_insert_descriptor(struct gatt_db_attribute *attrib,
|
||||
uint16_t handle,
|
||||
const bt_uuid_t *uuid,
|
||||
uint32_t permissions,
|
||||
gatt_db_read_t read_func,
|
||||
gatt_db_write_t write_func,
|
||||
void *user_data);
|
||||
|
||||
struct gatt_db_attribute *
|
||||
gatt_db_service_add_included(struct gatt_db_attribute *attrib,
|
||||
struct gatt_db_attribute *include);
|
||||
|
||||
bool gatt_db_service_set_active(struct gatt_db_attribute *attrib, bool active);
|
||||
bool gatt_db_service_get_active(struct gatt_db_attribute *attrib);
|
||||
|
||||
bool gatt_db_service_set_claimed(struct gatt_db_attribute *attrib,
|
||||
bool claimed);
|
||||
bool gatt_db_service_get_claimed(struct gatt_db_attribute *attrib);
|
||||
|
||||
typedef void (*gatt_db_attribute_cb_t)(struct gatt_db_attribute *attrib,
|
||||
void *user_data);
|
||||
|
||||
void gatt_db_read_by_group_type(struct gatt_db *db, uint16_t start_handle,
|
||||
uint16_t end_handle,
|
||||
const bt_uuid_t type,
|
||||
struct queue *queue);
|
||||
|
||||
unsigned int gatt_db_find_by_type(struct gatt_db *db, uint16_t start_handle,
|
||||
uint16_t end_handle,
|
||||
const bt_uuid_t *type,
|
||||
gatt_db_attribute_cb_t func,
|
||||
void *user_data);
|
||||
|
||||
unsigned int gatt_db_find_by_type_value(struct gatt_db *db,
|
||||
uint16_t start_handle,
|
||||
uint16_t end_handle,
|
||||
const bt_uuid_t *type,
|
||||
const void *value,
|
||||
size_t value_len,
|
||||
gatt_db_attribute_cb_t func,
|
||||
void *user_data);
|
||||
|
||||
void gatt_db_read_by_type(struct gatt_db *db, uint16_t start_handle,
|
||||
uint16_t end_handle,
|
||||
const bt_uuid_t type,
|
||||
struct queue *queue);
|
||||
|
||||
void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle,
|
||||
uint16_t end_handle,
|
||||
struct queue *queue);
|
||||
|
||||
|
||||
void gatt_db_foreach_service(struct gatt_db *db, const bt_uuid_t *uuid,
|
||||
gatt_db_attribute_cb_t func,
|
||||
void *user_data);
|
||||
void gatt_db_foreach_service_in_range(struct gatt_db *db,
|
||||
const bt_uuid_t *uuid,
|
||||
gatt_db_attribute_cb_t func,
|
||||
void *user_data,
|
||||
uint16_t start_handle,
|
||||
uint16_t end_handle);
|
||||
|
||||
void gatt_db_service_foreach(struct gatt_db_attribute *attrib,
|
||||
const bt_uuid_t *uuid,
|
||||
gatt_db_attribute_cb_t func,
|
||||
void *user_data);
|
||||
void gatt_db_service_foreach_char(struct gatt_db_attribute *attrib,
|
||||
gatt_db_attribute_cb_t func,
|
||||
void *user_data);
|
||||
void gatt_db_service_foreach_desc(struct gatt_db_attribute *attrib,
|
||||
gatt_db_attribute_cb_t func,
|
||||
void *user_data);
|
||||
void gatt_db_service_foreach_incl(struct gatt_db_attribute *attrib,
|
||||
gatt_db_attribute_cb_t func,
|
||||
void *user_data);
|
||||
|
||||
typedef void (*gatt_db_destroy_func_t)(void *user_data);
|
||||
|
||||
unsigned int gatt_db_register(struct gatt_db *db,
|
||||
gatt_db_attribute_cb_t service_added,
|
||||
gatt_db_attribute_cb_t service_removed,
|
||||
void *user_data,
|
||||
gatt_db_destroy_func_t destroy);
|
||||
bool gatt_db_unregister(struct gatt_db *db, unsigned int id);
|
||||
|
||||
struct gatt_db_attribute *gatt_db_get_attribute(struct gatt_db *db,
|
||||
uint16_t handle);
|
||||
|
||||
struct gatt_db_attribute *gatt_db_get_service_with_uuid(struct gatt_db *db,
|
||||
const bt_uuid_t *uuid);
|
||||
|
||||
const bt_uuid_t *gatt_db_attribute_get_type(
|
||||
const struct gatt_db_attribute *attrib);
|
||||
|
||||
uint16_t gatt_db_attribute_get_handle(const struct gatt_db_attribute *attrib);
|
||||
|
||||
bool gatt_db_attribute_get_service_uuid(const struct gatt_db_attribute *attrib,
|
||||
bt_uuid_t *uuid);
|
||||
|
||||
bool gatt_db_attribute_get_service_handles(
|
||||
const struct gatt_db_attribute *attrib,
|
||||
uint16_t *start_handle,
|
||||
uint16_t *end_handle);
|
||||
|
||||
bool gatt_db_attribute_get_service_data(const struct gatt_db_attribute *attrib,
|
||||
uint16_t *start_handle,
|
||||
uint16_t *end_handle,
|
||||
bool *primary,
|
||||
bt_uuid_t *uuid);
|
||||
|
||||
bool gatt_db_attribute_get_char_data(const struct gatt_db_attribute *attrib,
|
||||
uint16_t *handle,
|
||||
uint16_t *value_handle,
|
||||
uint8_t *properties,
|
||||
bt_uuid_t *uuid);
|
||||
|
||||
bool gatt_db_attribute_get_incl_data(const struct gatt_db_attribute *attrib,
|
||||
uint16_t *handle,
|
||||
uint16_t *start_handle,
|
||||
uint16_t *end_handle);
|
||||
|
||||
uint32_t
|
||||
gatt_db_attribute_get_permissions(const struct gatt_db_attribute *attrib);
|
||||
|
||||
typedef void (*gatt_db_attribute_read_t) (struct gatt_db_attribute *attrib,
|
||||
int err, const uint8_t *value,
|
||||
size_t length, void *user_data);
|
||||
|
||||
bool gatt_db_attribute_read(struct gatt_db_attribute *attrib, uint16_t offset,
|
||||
uint8_t opcode, struct bt_att *att,
|
||||
gatt_db_attribute_read_t func, void *user_data);
|
||||
|
||||
bool gatt_db_attribute_read_result(struct gatt_db_attribute *attrib,
|
||||
unsigned int id, int err,
|
||||
const uint8_t *value, size_t length);
|
||||
|
||||
typedef void (*gatt_db_attribute_write_t) (struct gatt_db_attribute *attrib,
|
||||
int err, void *user_data);
|
||||
|
||||
bool gatt_db_attribute_write(struct gatt_db_attribute *attrib, uint16_t offset,
|
||||
const uint8_t *value, size_t len,
|
||||
uint8_t opcode, struct bt_att *att,
|
||||
gatt_db_attribute_write_t func,
|
||||
void *user_data);
|
||||
|
||||
bool gatt_db_attribute_write_result(struct gatt_db_attribute *attrib,
|
||||
unsigned int id, int err);
|
||||
|
||||
bool gatt_db_attribute_reset(struct gatt_db_attribute *attrib);
|
||||
1580
gatt-helpers.c
Normal file
1580
gatt-helpers.c
Normal file
File diff suppressed because it is too large
Load Diff
116
gatt-helpers.h
Normal file
116
gatt-helpers.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2014 Google Inc.
|
||||
*
|
||||
*
|
||||
* This library 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 library 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 this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/* This file defines helpers for performing client-side procedures defined by
|
||||
* the Generic Attribute Profile.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct bt_gatt_result;
|
||||
|
||||
struct bt_gatt_iter {
|
||||
struct bt_gatt_result *result;
|
||||
uint16_t pos;
|
||||
};
|
||||
|
||||
unsigned int bt_gatt_result_service_count(struct bt_gatt_result *result);
|
||||
unsigned int bt_gatt_result_characteristic_count(struct bt_gatt_result *result);
|
||||
unsigned int bt_gatt_result_descriptor_count(struct bt_gatt_result *result);
|
||||
unsigned int bt_gatt_result_included_count(struct bt_gatt_result *result);
|
||||
|
||||
bool bt_gatt_iter_init(struct bt_gatt_iter *iter, struct bt_gatt_result *result);
|
||||
bool bt_gatt_iter_next_service(struct bt_gatt_iter *iter,
|
||||
uint16_t *start_handle, uint16_t *end_handle,
|
||||
uint8_t uuid[16]);
|
||||
bool bt_gatt_iter_next_characteristic(struct bt_gatt_iter *iter,
|
||||
uint16_t *start_handle, uint16_t *end_handle,
|
||||
uint16_t *value_handle, uint8_t *properties,
|
||||
uint8_t uuid[16]);
|
||||
bool bt_gatt_iter_next_descriptor(struct bt_gatt_iter *iter, uint16_t *handle,
|
||||
uint8_t uuid[16]);
|
||||
bool bt_gatt_iter_next_included_service(struct bt_gatt_iter *iter,
|
||||
uint16_t *handle, uint16_t *start_handle,
|
||||
uint16_t *end_handle, uint8_t uuid[16]);
|
||||
bool bt_gatt_iter_next_read_by_type(struct bt_gatt_iter *iter,
|
||||
uint16_t *handle, uint16_t *length,
|
||||
const uint8_t **value);
|
||||
|
||||
typedef void (*bt_gatt_destroy_func_t)(void *user_data);
|
||||
|
||||
typedef void (*bt_gatt_result_callback_t)(bool success, uint8_t att_ecode,
|
||||
void *user_data);
|
||||
typedef void (*bt_gatt_request_callback_t)(bool success, uint8_t att_ecode,
|
||||
struct bt_gatt_result *result,
|
||||
void *user_data);
|
||||
|
||||
struct bt_gatt_request;
|
||||
|
||||
struct bt_gatt_request *bt_gatt_request_ref(struct bt_gatt_request *req);
|
||||
void bt_gatt_request_unref(struct bt_gatt_request *req);
|
||||
void bt_gatt_request_cancel(struct bt_gatt_request *req);
|
||||
|
||||
unsigned int bt_gatt_exchange_mtu(struct bt_att *att, uint16_t client_rx_mtu,
|
||||
bt_gatt_result_callback_t callback,
|
||||
void *user_data,
|
||||
bt_gatt_destroy_func_t destroy);
|
||||
|
||||
struct bt_gatt_request *bt_gatt_discover_all_primary_services(
|
||||
struct bt_att *att, bt_uuid_t *uuid,
|
||||
bt_gatt_request_callback_t callback,
|
||||
void *user_data,
|
||||
bt_gatt_destroy_func_t destroy);
|
||||
struct bt_gatt_request *bt_gatt_discover_primary_services(
|
||||
struct bt_att *att, bt_uuid_t *uuid,
|
||||
uint16_t start, uint16_t end,
|
||||
bt_gatt_request_callback_t callback,
|
||||
void *user_data,
|
||||
bt_gatt_destroy_func_t destroy);
|
||||
struct bt_gatt_request *bt_gatt_discover_secondary_services(
|
||||
struct bt_att *att, bt_uuid_t *uuid,
|
||||
uint16_t start, uint16_t end,
|
||||
bt_gatt_request_callback_t callback,
|
||||
void *user_data,
|
||||
bt_gatt_destroy_func_t destroy);
|
||||
struct bt_gatt_request *bt_gatt_discover_included_services(struct bt_att *att,
|
||||
uint16_t start, uint16_t end,
|
||||
bt_gatt_request_callback_t callback,
|
||||
void *user_data,
|
||||
bt_gatt_destroy_func_t destroy);
|
||||
struct bt_gatt_request *bt_gatt_discover_characteristics(struct bt_att *att,
|
||||
uint16_t start, uint16_t end,
|
||||
bt_gatt_request_callback_t callback,
|
||||
void *user_data,
|
||||
bt_gatt_destroy_func_t destroy);
|
||||
struct bt_gatt_request *bt_gatt_discover_descriptors(struct bt_att *att,
|
||||
uint16_t start, uint16_t end,
|
||||
bt_gatt_request_callback_t callback,
|
||||
void *user_data,
|
||||
bt_gatt_destroy_func_t destroy);
|
||||
|
||||
bool bt_gatt_read_by_type(struct bt_att *att, uint16_t start, uint16_t end,
|
||||
const bt_uuid_t *uuid,
|
||||
bt_gatt_request_callback_t callback,
|
||||
void *user_data,
|
||||
bt_gatt_destroy_func_t destroy);
|
||||
242
hci_lib.h
Normal file
242
hci_lib.h
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2000-2001 Qualcomm Incorporated
|
||||
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
* Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __HCI_LIB_H
|
||||
#define __HCI_LIB_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct hci_request {
|
||||
uint16_t ogf;
|
||||
uint16_t ocf;
|
||||
int event;
|
||||
void *cparam;
|
||||
int clen;
|
||||
void *rparam;
|
||||
int rlen;
|
||||
};
|
||||
|
||||
struct hci_version {
|
||||
uint16_t manufacturer;
|
||||
uint8_t hci_ver;
|
||||
uint16_t hci_rev;
|
||||
uint8_t lmp_ver;
|
||||
uint16_t lmp_subver;
|
||||
};
|
||||
|
||||
int hci_open_dev(int dev_id);
|
||||
int hci_close_dev(int dd);
|
||||
int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param);
|
||||
int hci_send_req(int dd, struct hci_request *req, int timeout);
|
||||
|
||||
int hci_create_connection(int dd, const bdaddr_t *bdaddr, uint16_t ptype, uint16_t clkoffset, uint8_t rswitch, uint16_t *handle, int to);
|
||||
int hci_disconnect(int dd, uint16_t handle, uint8_t reason, int to);
|
||||
|
||||
int hci_inquiry(int dev_id, int len, int num_rsp, const uint8_t *lap, inquiry_info **ii, long flags);
|
||||
int hci_devinfo(int dev_id, struct hci_dev_info *di);
|
||||
int hci_devba(int dev_id, bdaddr_t *bdaddr);
|
||||
int hci_devid(const char *str);
|
||||
|
||||
int hci_read_local_name(int dd, int len, char *name, int to);
|
||||
int hci_write_local_name(int dd, const char *name, int to);
|
||||
int hci_read_remote_name(int dd, const bdaddr_t *bdaddr, int len, char *name, int to);
|
||||
int hci_read_remote_name_with_clock_offset(int dd, const bdaddr_t *bdaddr, uint8_t pscan_rep_mode, uint16_t clkoffset, int len, char *name, int to);
|
||||
int hci_read_remote_name_cancel(int dd, const bdaddr_t *bdaddr, int to);
|
||||
int hci_read_remote_version(int dd, uint16_t handle, struct hci_version *ver, int to);
|
||||
int hci_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to);
|
||||
int hci_read_remote_ext_features(int dd, uint16_t handle, uint8_t page, uint8_t *max_page, uint8_t *features, int to);
|
||||
int hci_read_clock_offset(int dd, uint16_t handle, uint16_t *clkoffset, int to);
|
||||
int hci_read_local_version(int dd, struct hci_version *ver, int to);
|
||||
int hci_read_local_commands(int dd, uint8_t *commands, int to);
|
||||
int hci_read_local_features(int dd, uint8_t *features, int to);
|
||||
int hci_read_local_ext_features(int dd, uint8_t page, uint8_t *max_page, uint8_t *features, int to);
|
||||
int hci_read_bd_addr(int dd, bdaddr_t *bdaddr, int to);
|
||||
int hci_read_class_of_dev(int dd, uint8_t *cls, int to);
|
||||
int hci_write_class_of_dev(int dd, uint32_t cls, int to);
|
||||
int hci_read_voice_setting(int dd, uint16_t *vs, int to);
|
||||
int hci_write_voice_setting(int dd, uint16_t vs, int to);
|
||||
int hci_read_current_iac_lap(int dd, uint8_t *num_iac, uint8_t *lap, int to);
|
||||
int hci_write_current_iac_lap(int dd, uint8_t num_iac, uint8_t *lap, int to);
|
||||
int hci_read_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to);
|
||||
int hci_write_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t *key, int to);
|
||||
int hci_delete_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to);
|
||||
int hci_authenticate_link(int dd, uint16_t handle, int to);
|
||||
int hci_encrypt_link(int dd, uint16_t handle, uint8_t encrypt, int to);
|
||||
int hci_change_link_key(int dd, uint16_t handle, int to);
|
||||
int hci_switch_role(int dd, bdaddr_t *bdaddr, uint8_t role, int to);
|
||||
int hci_park_mode(int dd, uint16_t handle, uint16_t max_interval, uint16_t min_interval, int to);
|
||||
int hci_exit_park_mode(int dd, uint16_t handle, int to);
|
||||
int hci_read_inquiry_scan_type(int dd, uint8_t *type, int to);
|
||||
int hci_write_inquiry_scan_type(int dd, uint8_t type, int to);
|
||||
int hci_read_inquiry_mode(int dd, uint8_t *mode, int to);
|
||||
int hci_write_inquiry_mode(int dd, uint8_t mode, int to);
|
||||
int hci_read_afh_mode(int dd, uint8_t *mode, int to);
|
||||
int hci_write_afh_mode(int dd, uint8_t mode, int to);
|
||||
int hci_read_ext_inquiry_response(int dd, uint8_t *fec, uint8_t *data, int to);
|
||||
int hci_write_ext_inquiry_response(int dd, uint8_t fec, uint8_t *data, int to);
|
||||
int hci_read_simple_pairing_mode(int dd, uint8_t *mode, int to);
|
||||
int hci_write_simple_pairing_mode(int dd, uint8_t mode, int to);
|
||||
int hci_read_local_oob_data(int dd, uint8_t *hash, uint8_t *randomizer, int to);
|
||||
int hci_read_inq_response_tx_power_level(int dd, int8_t *level, int to);
|
||||
int hci_read_inquiry_transmit_power_level(int dd, int8_t *level, int to);
|
||||
int hci_write_inquiry_transmit_power_level(int dd, int8_t level, int to);
|
||||
int hci_read_transmit_power_level(int dd, uint16_t handle, uint8_t type, int8_t *level, int to);
|
||||
int hci_read_link_policy(int dd, uint16_t handle, uint16_t *policy, int to);
|
||||
int hci_write_link_policy(int dd, uint16_t handle, uint16_t policy, int to);
|
||||
int hci_read_link_supervision_timeout(int dd, uint16_t handle, uint16_t *timeout, int to);
|
||||
int hci_write_link_supervision_timeout(int dd, uint16_t handle, uint16_t timeout, int to);
|
||||
int hci_set_afh_classification(int dd, uint8_t *map, int to);
|
||||
int hci_read_link_quality(int dd, uint16_t handle, uint8_t *link_quality, int to);
|
||||
int hci_read_rssi(int dd, uint16_t handle, int8_t *rssi, int to);
|
||||
int hci_read_afh_map(int dd, uint16_t handle, uint8_t *mode, uint8_t *map, int to);
|
||||
int hci_read_clock(int dd, uint16_t handle, uint8_t which, uint32_t *clock, uint16_t *accuracy, int to);
|
||||
|
||||
int hci_le_set_scan_enable(int dev_id, uint8_t enable, uint8_t filter_dup, int to);
|
||||
int hci_le_set_scan_parameters(int dev_id, uint8_t type, uint16_t interval,
|
||||
uint16_t window, uint8_t own_type,
|
||||
uint8_t filter, int to);
|
||||
int hci_le_set_advertise_enable(int dev_id, uint8_t enable, int to);
|
||||
int hci_le_create_conn(int dd, uint16_t interval, uint16_t window,
|
||||
uint8_t initiator_filter, uint8_t peer_bdaddr_type,
|
||||
bdaddr_t peer_bdaddr, uint8_t own_bdaddr_type,
|
||||
uint16_t min_interval, uint16_t max_interval,
|
||||
uint16_t latency, uint16_t supervision_timeout,
|
||||
uint16_t min_ce_length, uint16_t max_ce_length,
|
||||
uint16_t *handle, int to);
|
||||
int hci_le_conn_update(int dd, uint16_t handle, uint16_t min_interval,
|
||||
uint16_t max_interval, uint16_t latency,
|
||||
uint16_t supervision_timeout, int to);
|
||||
int hci_le_add_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to);
|
||||
int hci_le_rm_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to);
|
||||
int hci_le_read_white_list_size(int dd, uint8_t *size, int to);
|
||||
int hci_le_clear_white_list(int dd, int to);
|
||||
int hci_le_add_resolving_list(int dd, const bdaddr_t *bdaddr, uint8_t type,
|
||||
uint8_t *peer_irk, uint8_t *local_irk, int to);
|
||||
int hci_le_rm_resolving_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to);
|
||||
int hci_le_clear_resolving_list(int dd, int to);
|
||||
int hci_le_read_resolving_list_size(int dd, uint8_t *size, int to);
|
||||
int hci_le_set_address_resolution_enable(int dev_id, uint8_t enable, int to);
|
||||
int hci_le_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to);
|
||||
|
||||
int hci_for_each_dev(int flag, int(*func)(int dd, int dev_id, long arg), long arg);
|
||||
int hci_get_route(bdaddr_t *bdaddr);
|
||||
|
||||
char *hci_bustostr(int bus);
|
||||
char *hci_typetostr(int type);
|
||||
char *hci_dtypetostr(int type);
|
||||
char *hci_dflagstostr(uint32_t flags);
|
||||
char *hci_ptypetostr(unsigned int ptype);
|
||||
int hci_strtoptype(char *str, unsigned int *val);
|
||||
char *hci_scoptypetostr(unsigned int ptype);
|
||||
int hci_strtoscoptype(char *str, unsigned int *val);
|
||||
char *hci_lptostr(unsigned int ptype);
|
||||
int hci_strtolp(char *str, unsigned int *val);
|
||||
char *hci_lmtostr(unsigned int ptype);
|
||||
int hci_strtolm(char *str, unsigned int *val);
|
||||
|
||||
char *hci_cmdtostr(unsigned int cmd);
|
||||
char *hci_commandstostr(uint8_t *commands, char *pref, int width);
|
||||
|
||||
char *hci_vertostr(unsigned int ver);
|
||||
int hci_strtover(char *str, unsigned int *ver);
|
||||
char *lmp_vertostr(unsigned int ver);
|
||||
int lmp_strtover(char *str, unsigned int *ver);
|
||||
char *pal_vertostr(unsigned int ver);
|
||||
int pal_strtover(char *str, unsigned int *ver);
|
||||
|
||||
char *lmp_featurestostr(uint8_t *features, char *pref, int width);
|
||||
|
||||
static inline void hci_set_bit(int nr, void *addr)
|
||||
{
|
||||
*((uint32_t *) addr + (nr >> 5)) |= (1 << (nr & 31));
|
||||
}
|
||||
|
||||
static inline void hci_clear_bit(int nr, void *addr)
|
||||
{
|
||||
*((uint32_t *) addr + (nr >> 5)) &= ~(1 << (nr & 31));
|
||||
}
|
||||
|
||||
static inline int hci_test_bit(int nr, void *addr)
|
||||
{
|
||||
return *((uint32_t *) addr + (nr >> 5)) & (1 << (nr & 31));
|
||||
}
|
||||
|
||||
/* HCI filter tools */
|
||||
static inline void hci_filter_clear(struct hci_filter *f)
|
||||
{
|
||||
memset(f, 0, sizeof(*f));
|
||||
}
|
||||
static inline void hci_filter_set_ptype(int t, struct hci_filter *f)
|
||||
{
|
||||
hci_set_bit((t == HCI_VENDOR_PKT) ? 0 : (t & HCI_FLT_TYPE_BITS), &f->type_mask);
|
||||
}
|
||||
static inline void hci_filter_clear_ptype(int t, struct hci_filter *f)
|
||||
{
|
||||
hci_clear_bit((t == HCI_VENDOR_PKT) ? 0 : (t & HCI_FLT_TYPE_BITS), &f->type_mask);
|
||||
}
|
||||
static inline int hci_filter_test_ptype(int t, struct hci_filter *f)
|
||||
{
|
||||
return hci_test_bit((t == HCI_VENDOR_PKT) ? 0 : (t & HCI_FLT_TYPE_BITS), &f->type_mask);
|
||||
}
|
||||
static inline void hci_filter_all_ptypes(struct hci_filter *f)
|
||||
{
|
||||
memset((void *) &f->type_mask, 0xff, sizeof(f->type_mask));
|
||||
}
|
||||
static inline void hci_filter_set_event(int e, struct hci_filter *f)
|
||||
{
|
||||
hci_set_bit((e & HCI_FLT_EVENT_BITS), &f->event_mask);
|
||||
}
|
||||
static inline void hci_filter_clear_event(int e, struct hci_filter *f)
|
||||
{
|
||||
hci_clear_bit((e & HCI_FLT_EVENT_BITS), &f->event_mask);
|
||||
}
|
||||
static inline int hci_filter_test_event(int e, struct hci_filter *f)
|
||||
{
|
||||
return hci_test_bit((e & HCI_FLT_EVENT_BITS), &f->event_mask);
|
||||
}
|
||||
static inline void hci_filter_all_events(struct hci_filter *f)
|
||||
{
|
||||
memset((void *) f->event_mask, 0xff, sizeof(f->event_mask));
|
||||
}
|
||||
static inline void hci_filter_set_opcode(int opcode, struct hci_filter *f)
|
||||
{
|
||||
f->opcode = opcode;
|
||||
}
|
||||
static inline void hci_filter_clear_opcode(struct hci_filter *f)
|
||||
{
|
||||
f->opcode = 0;
|
||||
}
|
||||
static inline int hci_filter_test_opcode(int opcode, struct hci_filter *f)
|
||||
{
|
||||
return (f->opcode == opcode);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __HCI_LIB_H */
|
||||
423
io-mainloop.c
Normal file
423
io-mainloop.c
Normal file
@@ -0,0 +1,423 @@
|
||||
/**
|
||||
* @file io-mainloop.c
|
||||
* @brief set of function to manage io part of the mainloop feature
|
||||
* @see mainloop.c
|
||||
* @author Gilbert Brault
|
||||
* @copyright Gilbert Brault 2015
|
||||
* the original work comes from bluez v5.39
|
||||
* value add: documenting main features
|
||||
*
|
||||
*/
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2012-2014 Intel Corporation. All rights reserved.
|
||||
*
|
||||
*
|
||||
* This library 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 library 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 this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "mainloop.h"
|
||||
#include "util.h"
|
||||
#include "io.h"
|
||||
|
||||
/**
|
||||
* @brief data structure to manage io
|
||||
*/
|
||||
struct io {
|
||||
int ref_count; /**< number of references to the data structure */
|
||||
int fd; /**< file descriptor */
|
||||
uint32_t events; /**< epoll events (might be ored) */
|
||||
bool close_on_destroy; /**< do you need to close the underlying socket on destroy? */
|
||||
io_callback_func_t read_callback; /**< read call back */
|
||||
io_destroy_func_t read_destroy; /**< data management for read */
|
||||
void *read_data; /**< user pointer for read data */
|
||||
io_callback_func_t write_callback; /**< write call back*/
|
||||
io_destroy_func_t write_destroy; /**< data management for write */
|
||||
void *write_data; /**< user pointer for write data */
|
||||
io_callback_func_t disconnect_callback; /**< call back on disconnect */
|
||||
io_destroy_func_t disconnect_destroy; /**< data management at disconnect */
|
||||
void *disconnect_data; /**< user pointer for disconnect */
|
||||
};
|
||||
|
||||
/**
|
||||
* Increment &io->ref_count
|
||||
* @param io point to io data structure
|
||||
* @return
|
||||
*/
|
||||
static struct io *io_ref(struct io *io)
|
||||
{
|
||||
if (!io)
|
||||
return NULL;
|
||||
|
||||
__sync_fetch_and_add(&io->ref_count, 1);
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement &io->ref_count and free io data structure when count reaches 0
|
||||
* @param io point to io data structure
|
||||
* @return
|
||||
*/
|
||||
static void io_unref(struct io *io)
|
||||
{
|
||||
if (!io)
|
||||
return;
|
||||
|
||||
if (__sync_sub_and_fetch(&io->ref_count, 1))
|
||||
return;
|
||||
|
||||
free(io);
|
||||
}
|
||||
|
||||
/**
|
||||
* io data structure house keeping
|
||||
* @param user_data pointer to io data structure
|
||||
*/
|
||||
static void io_cleanup(void *user_data)
|
||||
{
|
||||
struct io *io = user_data;
|
||||
|
||||
if (io->write_destroy)
|
||||
io->write_destroy(io->write_data);
|
||||
|
||||
if (io->read_destroy)
|
||||
io->read_destroy(io->read_data);
|
||||
|
||||
if (io->disconnect_destroy)
|
||||
io->disconnect_destroy(io->disconnect_data);
|
||||
|
||||
if (io->close_on_destroy)
|
||||
close(io->fd);
|
||||
|
||||
io->fd = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* depending on events epoll event and read, write or disconnect prepare for the appropriate call back calling mainloop_modify_fd
|
||||
*
|
||||
* epoll event value (some 'ored' combination allowed) EPOLLRDHUP | EPOLLHUP | EPOLLERR | EPOLLIN | EPOLLOUT )
|
||||
*
|
||||
* @param fd file descriptor data structure (can be socket)
|
||||
* @param events epoll event
|
||||
* @param user_data pointer to io data structure
|
||||
*/
|
||||
static void io_callback(int fd, uint32_t events, void *user_data)
|
||||
{
|
||||
struct io *io = user_data;
|
||||
|
||||
io_ref(io);
|
||||
|
||||
if ((events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))) {
|
||||
io->read_callback = NULL;
|
||||
io->write_callback = NULL;
|
||||
|
||||
if (!io->disconnect_callback) {
|
||||
mainloop_remove_fd(io->fd);
|
||||
io_unref(io);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!io->disconnect_callback(io, io->disconnect_data)) {
|
||||
if (io->disconnect_destroy)
|
||||
io->disconnect_destroy(io->disconnect_data);
|
||||
|
||||
io->disconnect_callback = NULL;
|
||||
io->disconnect_destroy = NULL;
|
||||
io->disconnect_data = NULL;
|
||||
|
||||
io->events &= ~EPOLLRDHUP;
|
||||
|
||||
mainloop_modify_fd(io->fd, io->events);
|
||||
}
|
||||
}
|
||||
|
||||
if ((events & EPOLLIN) && io->read_callback) {
|
||||
if (!io->read_callback(io, io->read_data)) {
|
||||
if (io->read_destroy)
|
||||
io->read_destroy(io->read_data);
|
||||
|
||||
io->read_callback = NULL;
|
||||
io->read_destroy = NULL;
|
||||
io->read_data = NULL;
|
||||
|
||||
io->events &= ~EPOLLIN;
|
||||
|
||||
mainloop_modify_fd(io->fd, io->events);
|
||||
}
|
||||
}
|
||||
|
||||
if ((events & EPOLLOUT) && io->write_callback) {
|
||||
if (!io->write_callback(io, io->write_data)) {
|
||||
if (io->write_destroy)
|
||||
io->write_destroy(io->write_data);
|
||||
|
||||
io->write_callback = NULL;
|
||||
io->write_destroy = NULL;
|
||||
io->write_data = NULL;
|
||||
|
||||
io->events &= ~EPOLLOUT;
|
||||
|
||||
mainloop_modify_fd(io->fd, io->events);
|
||||
}
|
||||
}
|
||||
|
||||
io_unref(io);
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new io data structure
|
||||
*
|
||||
* @param fd file descriptor (includes socket)
|
||||
* @return NULL if error or io data structure
|
||||
*/
|
||||
struct io *io_new(int fd)
|
||||
{
|
||||
struct io *io;
|
||||
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
|
||||
io = new0(struct io, 1);
|
||||
if (!io)
|
||||
return NULL;
|
||||
|
||||
io->fd = fd;
|
||||
io->events = 0;
|
||||
io->close_on_destroy = false;
|
||||
|
||||
if (mainloop_add_fd(io->fd, io->events, io_callback,
|
||||
io, io_cleanup) < 0) {
|
||||
free(io);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return io_ref(io);
|
||||
}
|
||||
|
||||
/**
|
||||
* remove the fd from the 'mainloop' and reset all io call back, unref the io data structure
|
||||
*
|
||||
* @param io pointer to the io data structure
|
||||
*/
|
||||
void io_destroy(struct io *io)
|
||||
{
|
||||
if (!io)
|
||||
return;
|
||||
|
||||
io->read_callback = NULL;
|
||||
io->write_callback = NULL;
|
||||
io->disconnect_callback = NULL;
|
||||
|
||||
mainloop_remove_fd(io->fd);
|
||||
|
||||
io_unref(io);
|
||||
}
|
||||
|
||||
/**
|
||||
* return the fd (file descriptor, can be a socket) attached to the io data structure
|
||||
*
|
||||
* @param io pointer to io data structure
|
||||
* @return
|
||||
*/
|
||||
int io_get_fd(struct io *io)
|
||||
{
|
||||
if (!io)
|
||||
return -ENOTCONN;
|
||||
|
||||
return io->fd;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param io
|
||||
* @param do_close
|
||||
* @return
|
||||
*/
|
||||
bool io_set_close_on_destroy(struct io *io, bool do_close)
|
||||
{
|
||||
if (!io)
|
||||
return false;
|
||||
|
||||
io->close_on_destroy = do_close;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param io
|
||||
* @param callback
|
||||
* @param user_data
|
||||
* @param destroy
|
||||
* @return
|
||||
*/
|
||||
bool io_set_read_handler(struct io *io, io_callback_func_t callback,
|
||||
void *user_data, io_destroy_func_t destroy)
|
||||
{
|
||||
uint32_t events;
|
||||
|
||||
if (!io || io->fd < 0)
|
||||
return false;
|
||||
|
||||
if (io->read_destroy)
|
||||
io->read_destroy(io->read_data);
|
||||
|
||||
if (callback)
|
||||
events = io->events | EPOLLIN;
|
||||
else
|
||||
events = io->events & ~EPOLLIN;
|
||||
|
||||
io->read_callback = callback;
|
||||
io->read_destroy = destroy;
|
||||
io->read_data = user_data;
|
||||
|
||||
if (events == io->events)
|
||||
return true;
|
||||
|
||||
if (mainloop_modify_fd(io->fd, events) < 0)
|
||||
return false;
|
||||
|
||||
io->events = events;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param io
|
||||
* @param callback
|
||||
* @param user_data
|
||||
* @param destroy
|
||||
* @return
|
||||
*/
|
||||
bool io_set_write_handler(struct io *io, io_callback_func_t callback,
|
||||
void *user_data, io_destroy_func_t destroy)
|
||||
{
|
||||
uint32_t events;
|
||||
|
||||
if (!io || io->fd < 0)
|
||||
return false;
|
||||
|
||||
if (io->write_destroy)
|
||||
io->write_destroy(io->write_data);
|
||||
|
||||
if (callback)
|
||||
events = io->events | EPOLLOUT;
|
||||
else
|
||||
events = io->events & ~EPOLLOUT;
|
||||
|
||||
io->write_callback = callback;
|
||||
io->write_destroy = destroy;
|
||||
io->write_data = user_data;
|
||||
|
||||
if (events == io->events)
|
||||
return true;
|
||||
|
||||
if (mainloop_modify_fd(io->fd, events) < 0)
|
||||
return false;
|
||||
|
||||
io->events = events;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param io
|
||||
* @param callback
|
||||
* @param user_data
|
||||
* @param destroy
|
||||
* @return
|
||||
*/
|
||||
bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback,
|
||||
void *user_data, io_destroy_func_t destroy)
|
||||
{
|
||||
uint32_t events;
|
||||
|
||||
if (!io || io->fd < 0)
|
||||
return false;
|
||||
|
||||
if (io->disconnect_destroy)
|
||||
io->disconnect_destroy(io->disconnect_data);
|
||||
|
||||
if (callback)
|
||||
events = io->events | EPOLLRDHUP;
|
||||
else
|
||||
events = io->events & ~EPOLLRDHUP;
|
||||
|
||||
io->disconnect_callback = callback;
|
||||
io->disconnect_destroy = destroy;
|
||||
io->disconnect_data = user_data;
|
||||
|
||||
if (events == io->events)
|
||||
return true;
|
||||
|
||||
if (mainloop_modify_fd(io->fd, events) < 0)
|
||||
return false;
|
||||
|
||||
io->events = events;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* send data to the underlying socket (actual write)
|
||||
*
|
||||
* @param io io structure which describe the underlying socket and its context
|
||||
* @param iov pointer to pdu, pdu length structure
|
||||
* @param iovcnt number of pdu to send (iov is an array)
|
||||
* @return The return value is a count of bytes written, or -errno indicating an error.
|
||||
*/
|
||||
ssize_t io_send(struct io *io, const struct iovec *iov, int iovcnt)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
if (!io || io->fd < 0)
|
||||
return -ENOTCONN;
|
||||
|
||||
do {
|
||||
ret = writev(io->fd, iov, iovcnt);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
|
||||
if (ret < 0)
|
||||
return -errno;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param io
|
||||
* @return
|
||||
*/
|
||||
bool io_shutdown(struct io *io)
|
||||
{
|
||||
if (!io || io->fd < 0)
|
||||
return false;
|
||||
|
||||
return shutdown(io->fd, SHUT_RDWR) == 0;
|
||||
}
|
||||
47
io.h
Normal file
47
io.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2012-2014 Intel Corporation. All rights reserved.
|
||||
*
|
||||
*
|
||||
* This library 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 library 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 this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
typedef void (*io_destroy_func_t)(void *data);
|
||||
|
||||
struct io;
|
||||
|
||||
struct io *io_new(int fd);
|
||||
void io_destroy(struct io *io);
|
||||
|
||||
int io_get_fd(struct io *io);
|
||||
bool io_set_close_on_destroy(struct io *io, bool do_close);
|
||||
|
||||
ssize_t io_send(struct io *io, const struct iovec *iov, int iovcnt);
|
||||
bool io_shutdown(struct io *io);
|
||||
|
||||
typedef bool (*io_callback_func_t)(struct io *io, void *user_data);
|
||||
|
||||
bool io_set_read_handler(struct io *io, io_callback_func_t callback,
|
||||
void *user_data, io_destroy_func_t destroy);
|
||||
bool io_set_write_handler(struct io *io, io_callback_func_t callback,
|
||||
void *user_data, io_destroy_func_t destroy);
|
||||
bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback,
|
||||
void *user_data, io_destroy_func_t destroy);
|
||||
887
json.c
Normal file
887
json.c
Normal file
@@ -0,0 +1,887 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "json.h"
|
||||
|
||||
|
||||
/*
|
||||
read and traverse
|
||||
*/
|
||||
|
||||
static void skipWhiteSpaces(const char** pstr)
|
||||
{
|
||||
while (isspace(**pstr))
|
||||
++*pstr;
|
||||
}
|
||||
|
||||
static void fillError(const char* str, char* errorBuf, int errorBufSize)
|
||||
{
|
||||
static const char errorPrefix[] = "Parse error here: ";
|
||||
static const int errorPrefixLen = sizeof(errorPrefix);
|
||||
|
||||
snprintf(errorBuf, errorBufSize, "%s", errorPrefix);
|
||||
snprintf(errorBuf + errorPrefixLen - 1, errorBufSize - errorPrefixLen, "%s", str);
|
||||
}
|
||||
|
||||
static int parseChar(const char **source, char c)
|
||||
{
|
||||
skipWhiteSpaces(source);
|
||||
if (!**source || tolower(**source) != tolower(c))
|
||||
return -1;
|
||||
|
||||
++*source;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int createStringCopy(const char *source, int sourceLen, char **target)
|
||||
{
|
||||
if (sourceLen == -1)
|
||||
sourceLen = strlen(source);
|
||||
|
||||
*target = malloc(sourceLen + 1);
|
||||
|
||||
if (!*target)
|
||||
return -1;
|
||||
|
||||
memcpy(*target, source, sourceLen);
|
||||
(*target)[sourceLen] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int parseQuotedString(const char **source, char **target)
|
||||
{
|
||||
const char *begin;
|
||||
int strLen;
|
||||
|
||||
skipWhiteSpaces(source);
|
||||
if (parseChar(source, '"') != 0)
|
||||
return -1;
|
||||
|
||||
begin = *source;
|
||||
while (**source != '"' && **source)
|
||||
++*source;
|
||||
|
||||
if (!**source)
|
||||
return -1;
|
||||
|
||||
strLen = *source - begin;
|
||||
if (createStringCopy(begin, strLen, target) != 0)
|
||||
return -1;
|
||||
|
||||
++*source;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int matchString(const char **source, const char *pattern)
|
||||
{
|
||||
while (*pattern)
|
||||
{
|
||||
if (parseChar(source, *pattern) != 0)
|
||||
return -1;
|
||||
++pattern;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reallocObject(struct JsonVal *val)
|
||||
{
|
||||
if (!(val->u.object.keys = realloc(
|
||||
val->u.object.keys,
|
||||
(val->u.object.len + 1) * sizeof(*val->u.object.keys))))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(val->u.object.values = realloc(
|
||||
val->u.object.values,
|
||||
(val->u.object.len + 1) * sizeof(*val->u.object.values))))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
val->u.object.len++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reallocArray(struct JsonVal *val)
|
||||
{
|
||||
if (!(val->u.array.values = realloc(
|
||||
val->u.array.values,
|
||||
(val->u.array.len + 1) * sizeof(*val->u.array.values))))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
val->u.array.len++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parseObject(const char **source, struct JsonVal *value);
|
||||
static int parseValue(const char **source, struct JsonVal *val);
|
||||
|
||||
static int parseArrayEntry(const char **source, struct JsonVal *val)
|
||||
{
|
||||
if(reallocArray(val) != 0)
|
||||
return -1;
|
||||
|
||||
skipWhiteSpaces(source);
|
||||
return parseValue(source, val->u.array.values + val->u.array.len - 1);
|
||||
}
|
||||
|
||||
static int parseArray(const char **source, struct JsonVal *value)
|
||||
{
|
||||
int finished = 0;
|
||||
|
||||
value->type = jsonArrayT;
|
||||
value->u.array.values = NULL;
|
||||
value->u.array.len = 0;
|
||||
|
||||
if (parseChar(source, '[') != 0)
|
||||
return -1;
|
||||
|
||||
skipWhiteSpaces(source);
|
||||
if (**source == ']')
|
||||
{
|
||||
++*source;
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (**source)
|
||||
{
|
||||
skipWhiteSpaces(source);
|
||||
if (parseArrayEntry(source, value) != 0)
|
||||
return -1;
|
||||
skipWhiteSpaces(source);
|
||||
if (**source == ',')
|
||||
{
|
||||
++*source;
|
||||
continue;
|
||||
}
|
||||
if (**source == ']')
|
||||
{
|
||||
++*source;
|
||||
finished = 1;
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return finished ? 0 : -1;
|
||||
}
|
||||
|
||||
static int parseString(const char **source, struct JsonVal *value)
|
||||
{
|
||||
value->type = jsonStringT;
|
||||
return parseQuotedString(source, &value->u.string);
|
||||
}
|
||||
|
||||
static int parseTrue(const char **source, struct JsonVal *value)
|
||||
{
|
||||
value->type = jsonTrueT;
|
||||
return matchString(source, "true");
|
||||
}
|
||||
|
||||
static int parseFalse(const char **source, struct JsonVal *value)
|
||||
{
|
||||
value->type = jsonFalseT;
|
||||
return matchString(source, "false");
|
||||
}
|
||||
|
||||
static int parseNum(const char **source, struct JsonVal *value)
|
||||
{
|
||||
value->type = jsonNumberT;
|
||||
value->u.number = strtold(*source, (char**)source);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parseNull(const char **source, struct JsonVal *value)
|
||||
{
|
||||
value->type = jsonNullT;
|
||||
return matchString(source, "null");
|
||||
}
|
||||
|
||||
static int (*getParser(char c))(const char **, struct JsonVal*)
|
||||
{
|
||||
switch (tolower(c))
|
||||
{
|
||||
case '[':
|
||||
return &parseArray;
|
||||
case '{':
|
||||
return &parseObject;
|
||||
case '"':
|
||||
return &parseString;
|
||||
case 'f':
|
||||
return &parseFalse;
|
||||
case 't':
|
||||
return &parseTrue;
|
||||
default:
|
||||
if (isdigit(c))
|
||||
return &parseNum;
|
||||
else if (tolower(c) == 'n')
|
||||
return &parseNull;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int parseValue(const char **source, struct JsonVal *val)
|
||||
{
|
||||
int (*valueParser)(const char **str, struct JsonVal *val) = getParser(**source);
|
||||
|
||||
if (valueParser == NULL)
|
||||
return -1;
|
||||
|
||||
return valueParser(source, val);
|
||||
}
|
||||
|
||||
static int parseKeyValue(const char **source, struct JsonVal *val)
|
||||
{
|
||||
assert(val->type == jsonObjectT);
|
||||
if(reallocObject(val) != 0)
|
||||
return -1;
|
||||
|
||||
if (parseQuotedString(source, val->u.object.keys + val->u.object.len - 1) != 0)
|
||||
return -1;
|
||||
|
||||
if (parseChar(source, ':') != 0)
|
||||
return -1;
|
||||
|
||||
skipWhiteSpaces(source);
|
||||
return parseValue(source, val->u.object.values + val->u.object.len - 1);
|
||||
}
|
||||
|
||||
static int parseObject(const char **source, struct JsonVal *value)
|
||||
{
|
||||
int finished = 0;
|
||||
|
||||
value->type = jsonObjectT;
|
||||
value->u.object.keys = NULL;
|
||||
value->u.object.values = NULL;
|
||||
value->u.object.len = 0;
|
||||
|
||||
skipWhiteSpaces(source);
|
||||
|
||||
if (parseChar(source, '{') != 0)
|
||||
return -1;
|
||||
|
||||
skipWhiteSpaces(source);
|
||||
if (**source == '}')
|
||||
{
|
||||
++*source;
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (**source)
|
||||
{
|
||||
skipWhiteSpaces(source);
|
||||
if (parseKeyValue(source, value) != 0)
|
||||
return -1;
|
||||
skipWhiteSpaces(source);
|
||||
if (**source == ',')
|
||||
{
|
||||
++*source;
|
||||
continue;
|
||||
}
|
||||
if (**source == '}')
|
||||
{
|
||||
++*source;
|
||||
finished = 1;
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return finished ? 0 : -1;
|
||||
}
|
||||
|
||||
struct JsonVal jsonParseString(const char *str, char *errorBuf, int errorBufSize)
|
||||
{
|
||||
struct JsonVal result;
|
||||
const char *_str = str;
|
||||
|
||||
result = (struct JsonVal){jsonNullT};
|
||||
if (parseObject(&_str, &result) != 0)
|
||||
fillError(_str, errorBuf, errorBufSize);
|
||||
else
|
||||
memset(errorBuf, 0, errorBufSize);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int JsonVal_isString(const struct JsonVal *val)
|
||||
{
|
||||
return val->type == jsonStringT;
|
||||
}
|
||||
|
||||
int JsonVal_isNumber(const struct JsonVal *val)
|
||||
{
|
||||
return val->type == jsonNumberT;
|
||||
}
|
||||
|
||||
int JsonVal_isObject(const struct JsonVal *val)
|
||||
{
|
||||
return val->type == jsonObjectT;
|
||||
}
|
||||
|
||||
int JsonVal_isArray(const struct JsonVal *val)
|
||||
{
|
||||
return val->type == jsonArrayT;
|
||||
}
|
||||
|
||||
int JsonVal_isTrue(const struct JsonVal *val)
|
||||
{
|
||||
return val->type == jsonTrueT;
|
||||
}
|
||||
|
||||
int JsonVal_isFalse(const struct JsonVal *val)
|
||||
{
|
||||
return val->type == jsonFalseT;
|
||||
}
|
||||
|
||||
int JsonVal_isNull(const struct JsonVal *val)
|
||||
{
|
||||
return val->type == jsonNullT;
|
||||
}
|
||||
|
||||
int JsonVal_arrayLen(struct JsonVal *val)
|
||||
{
|
||||
assert(val->type == jsonArrayT);
|
||||
if (val->type != jsonArrayT)
|
||||
return -1;
|
||||
|
||||
return val->u.array.len;
|
||||
}
|
||||
|
||||
struct JsonVal *JsonVal_arrayAt(struct JsonVal *val, int index)
|
||||
{
|
||||
assert(val->type == jsonArrayT);
|
||||
if (val->type != jsonArrayT || index >= val->u.array.len)
|
||||
return NULL;
|
||||
|
||||
return &val->u.array.values[index];
|
||||
}
|
||||
|
||||
struct JsonVal *JsonVal_getObjectValueByKey(const struct JsonVal *val, const char *key)
|
||||
{
|
||||
int i;
|
||||
|
||||
assert(val->type == jsonObjectT);
|
||||
if (val->type != jsonObjectT)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < val->u.object.len; ++i)
|
||||
{
|
||||
if (strcmp(key, val->u.object.keys[i]) == 0)
|
||||
return &val->u.object.values[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void JsonVal_forEachArrayElement(
|
||||
const struct JsonVal *val,
|
||||
void *ctx,
|
||||
void (*action)(void *, const struct JsonVal *))
|
||||
{
|
||||
int i;
|
||||
|
||||
assert(val->type == jsonArrayT);
|
||||
if (val->type != jsonArrayT)
|
||||
return;
|
||||
|
||||
for (i = 0; i < val->u.array.len; ++i)
|
||||
action(ctx, &val->u.array.values[i]);
|
||||
}
|
||||
|
||||
void JsonVal_forEachObjectElement(
|
||||
const struct JsonVal *val,
|
||||
void *ctx,
|
||||
void (*action)(void *, const char *key, const struct JsonVal *))
|
||||
{
|
||||
int i;
|
||||
|
||||
assert(val->type == jsonObjectT);
|
||||
if (val->type != jsonObjectT)
|
||||
return;
|
||||
|
||||
for (i = 0; i < val->u.object.len; ++i)
|
||||
action(ctx, val->u.object.keys[i], &val->u.object.values[i]);
|
||||
}
|
||||
|
||||
struct JsonVal jsonCreateObject()
|
||||
{
|
||||
struct JsonVal result;
|
||||
|
||||
result.type = jsonObjectT;
|
||||
memset(&result.u, 0, sizeof(result.u));
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct JsonVal *prepareNewObjectVal(struct JsonVal *val, const char *key)
|
||||
{
|
||||
struct JsonVal *newValPtr;
|
||||
|
||||
assert(val->type == jsonObjectT);
|
||||
if (val->type != jsonObjectT)
|
||||
return NULL;
|
||||
|
||||
if (reallocObject(val) != 0)
|
||||
return NULL;
|
||||
|
||||
if (createStringCopy(key, strlen(key), val->u.object.keys + val->u.object.len - 1) != 0)
|
||||
return NULL;
|
||||
|
||||
newValPtr = val->u.object.values + val->u.object.len - 1;
|
||||
memset(newValPtr, 0, sizeof(*newValPtr));
|
||||
|
||||
return newValPtr;
|
||||
}
|
||||
|
||||
struct JsonVal *JsonVal_objectAddString(struct JsonVal *val, const char *key, const char *value)
|
||||
{
|
||||
struct JsonVal *newValPtr = prepareNewObjectVal(val, key);
|
||||
|
||||
if (newValPtr == NULL)
|
||||
return NULL;
|
||||
|
||||
if (createStringCopy(value, strlen(value), &newValPtr->u.string) != 0)
|
||||
return NULL;
|
||||
|
||||
newValPtr->type = jsonStringT;
|
||||
|
||||
return newValPtr;
|
||||
}
|
||||
|
||||
struct JsonVal *JsonVal_objectAddNumber(struct JsonVal *val, const char *key, long double number)
|
||||
{
|
||||
struct JsonVal *newValPtr = prepareNewObjectVal(val, key);
|
||||
|
||||
if (newValPtr == NULL)
|
||||
return NULL;
|
||||
|
||||
newValPtr->u.number = number;
|
||||
newValPtr->type = jsonNumberT;
|
||||
|
||||
return newValPtr;
|
||||
}
|
||||
|
||||
struct JsonVal *JsonVal_objectAddObject(struct JsonVal *val, const char *key)
|
||||
{
|
||||
struct JsonVal *newValPtr = prepareNewObjectVal(val, key);
|
||||
|
||||
if (newValPtr == NULL)
|
||||
return NULL;
|
||||
|
||||
newValPtr->type = jsonObjectT;
|
||||
|
||||
return newValPtr;
|
||||
}
|
||||
|
||||
struct JsonVal *JsonVal_objectAddArray(struct JsonVal *val, const char *key)
|
||||
{
|
||||
struct JsonVal *newValPtr = prepareNewObjectVal(val, key);
|
||||
|
||||
if (newValPtr == NULL)
|
||||
return NULL;
|
||||
|
||||
newValPtr->type = jsonArrayT;
|
||||
|
||||
return newValPtr;
|
||||
}
|
||||
|
||||
struct JsonVal *JsonVal_objectAddTrue(struct JsonVal *val, const char *key)
|
||||
{
|
||||
struct JsonVal *newValPtr = prepareNewObjectVal(val, key);
|
||||
|
||||
if (newValPtr == NULL)
|
||||
return NULL;
|
||||
|
||||
newValPtr->type = jsonTrueT;
|
||||
|
||||
return newValPtr;
|
||||
}
|
||||
|
||||
struct JsonVal *JsonVal_objectAddFalse(struct JsonVal *val, const char *key)
|
||||
{
|
||||
struct JsonVal *newValPtr = prepareNewObjectVal(val, key);
|
||||
|
||||
if (newValPtr == NULL)
|
||||
return NULL;
|
||||
|
||||
newValPtr->type = jsonFalseT;
|
||||
|
||||
return newValPtr;
|
||||
}
|
||||
|
||||
struct JsonVal *JsonVal_objectAddNull(struct JsonVal *val, const char *key)
|
||||
{
|
||||
struct JsonVal *newValPtr = prepareNewObjectVal(val, key);
|
||||
|
||||
if (newValPtr == NULL)
|
||||
return NULL;
|
||||
|
||||
newValPtr->type = jsonNullT;
|
||||
|
||||
return newValPtr;
|
||||
}
|
||||
|
||||
static struct JsonVal *prepareNewArrayVal(struct JsonVal *val)
|
||||
{
|
||||
struct JsonVal *newValPtr;
|
||||
|
||||
assert(val->type == jsonArrayT);
|
||||
if (val->type != jsonArrayT)
|
||||
return NULL;
|
||||
|
||||
if (reallocArray(val) != 0)
|
||||
return NULL;
|
||||
|
||||
newValPtr = val->u.array.values + val->u.array.len - 1;
|
||||
memset(newValPtr, 0, sizeof(*newValPtr));
|
||||
|
||||
return newValPtr;
|
||||
}
|
||||
|
||||
struct JsonVal *JsonVal_arrayAddString(struct JsonVal *val, const char *value)
|
||||
{
|
||||
struct JsonVal *newValPtr = prepareNewArrayVal(val);
|
||||
|
||||
if (newValPtr == NULL)
|
||||
return NULL;
|
||||
|
||||
if (createStringCopy(value, strlen(value), &newValPtr->u.string) != 0)
|
||||
return NULL;
|
||||
|
||||
newValPtr->type = jsonStringT;
|
||||
|
||||
return newValPtr;
|
||||
}
|
||||
|
||||
struct JsonVal *JsonVal_arrayAddNumber(struct JsonVal *val, long double number)
|
||||
{
|
||||
struct JsonVal *newValPtr = prepareNewArrayVal(val);
|
||||
|
||||
if (newValPtr == NULL)
|
||||
return NULL;
|
||||
|
||||
newValPtr->type = jsonNumberT;
|
||||
newValPtr->u.number = number;
|
||||
|
||||
return newValPtr;
|
||||
}
|
||||
|
||||
struct JsonVal *JsonVal_arrayAddObject(struct JsonVal *val)
|
||||
{
|
||||
struct JsonVal *newValPtr = prepareNewArrayVal(val);
|
||||
|
||||
if (newValPtr == NULL)
|
||||
return NULL;
|
||||
|
||||
newValPtr->type = jsonObjectT;
|
||||
|
||||
return newValPtr;
|
||||
}
|
||||
|
||||
struct JsonVal *JsonVal_arrayAddArray(struct JsonVal *val)
|
||||
{
|
||||
struct JsonVal *newValPtr = prepareNewArrayVal(val);
|
||||
|
||||
if (newValPtr == NULL)
|
||||
return NULL;
|
||||
|
||||
newValPtr->type = jsonArrayT;
|
||||
|
||||
return newValPtr;
|
||||
}
|
||||
|
||||
struct JsonVal *JsonVal_arrayAddTrue(struct JsonVal *val)
|
||||
{
|
||||
struct JsonVal *newValPtr = prepareNewArrayVal(val);
|
||||
|
||||
if (newValPtr == NULL)
|
||||
return NULL;
|
||||
|
||||
newValPtr->type = jsonTrueT;
|
||||
|
||||
return newValPtr;
|
||||
}
|
||||
|
||||
struct JsonVal *JsonVal_arrayAddFalse(struct JsonVal *val)
|
||||
{
|
||||
struct JsonVal *newValPtr = prepareNewArrayVal(val);
|
||||
|
||||
if (newValPtr == NULL)
|
||||
return NULL;
|
||||
|
||||
newValPtr->type = jsonFalseT;
|
||||
|
||||
return newValPtr;
|
||||
}
|
||||
|
||||
struct JsonVal *JsonVal_arrayAddNull(struct JsonVal *val)
|
||||
{
|
||||
struct JsonVal *newValPtr = prepareNewArrayVal(val);
|
||||
|
||||
if (newValPtr == NULL)
|
||||
return NULL;
|
||||
|
||||
newValPtr->type = jsonNullT;
|
||||
|
||||
return newValPtr;
|
||||
}
|
||||
|
||||
/*
|
||||
write
|
||||
*/
|
||||
|
||||
struct Buf
|
||||
{
|
||||
char *buf;
|
||||
int spaceLeft;
|
||||
};
|
||||
|
||||
static const int kWriteBufSize = 1024;
|
||||
|
||||
static int writeToBuf(void *ctx, void *source, int len)
|
||||
{
|
||||
struct Buf *target = (struct Buf *)ctx;
|
||||
int bytesToWrite = len >= target->spaceLeft ? target->spaceLeft : len;
|
||||
|
||||
memcpy(target->buf, source, bytesToWrite);
|
||||
target->spaceLeft -= bytesToWrite;
|
||||
target->buf += bytesToWrite;
|
||||
|
||||
return bytesToWrite;
|
||||
}
|
||||
|
||||
static void writeChar(
|
||||
char c, void *ctx,
|
||||
char *writeBuf,
|
||||
int (*writeFunc)(void *ctx, void *buf, int len))
|
||||
{
|
||||
writeBuf[0] = c;
|
||||
writeFunc(ctx, writeBuf, 1);
|
||||
}
|
||||
|
||||
static void writeString(
|
||||
const char *s, void *ctx,
|
||||
char *writeBuf,
|
||||
int (*writeFunc)(void *ctx, void *buf, int len))
|
||||
{
|
||||
int writeLen = strlen(s);
|
||||
int bytesToWrite;
|
||||
|
||||
while (writeLen > 0)
|
||||
{
|
||||
bytesToWrite = writeLen < kWriteBufSize ? writeLen : kWriteBufSize;
|
||||
writeFunc(ctx, (void *)s, bytesToWrite);
|
||||
writeLen -= bytesToWrite;
|
||||
s += bytesToWrite;
|
||||
}
|
||||
}
|
||||
|
||||
static void writeObject(
|
||||
const struct JsonVal *val,
|
||||
void *ctx, char *writeBuf,
|
||||
int (*writeFunc)(void *ctx, void *buf, int len));
|
||||
|
||||
static void writeValue(
|
||||
const struct JsonVal *val,
|
||||
void *ctx, char *writeBuf,
|
||||
int (*writeFunc)(void *ctx, void *buf, int len));
|
||||
|
||||
static void writeQuotedString(
|
||||
const char *s, void *ctx,
|
||||
char *writeBuf,
|
||||
int (*writeFunc)(void *ctx, void *buf, int len))
|
||||
{
|
||||
writeBuf[0] = '"';
|
||||
writeFunc(ctx, writeBuf, 1);
|
||||
|
||||
writeString(s, ctx, writeBuf, writeFunc);
|
||||
|
||||
writeBuf[0] = '"';
|
||||
writeFunc(ctx, writeBuf, 1);
|
||||
}
|
||||
|
||||
static void writeNumber(
|
||||
long double num,
|
||||
void *ctx, char *writeBuf,
|
||||
int (*writeFunc)(void *ctx, void *buf, int len))
|
||||
{
|
||||
char *dot;
|
||||
char *end;
|
||||
|
||||
sprintf(writeBuf, "%.5Lf", num);
|
||||
dot = strchr(writeBuf, '.');
|
||||
end = writeBuf + strlen(writeBuf) - 1;
|
||||
|
||||
for (; end != dot; --end)
|
||||
if (*end != '0')
|
||||
break;
|
||||
|
||||
if (end == dot)
|
||||
*end = '\0';
|
||||
else
|
||||
*(++end) ='\0';
|
||||
|
||||
writeString(writeBuf, ctx, NULL, writeFunc);
|
||||
}
|
||||
|
||||
static void writeArray(
|
||||
const struct JsonVal *val,
|
||||
void *ctx, char *writeBuf,
|
||||
int (*writeFunc)(void *ctx, void *buf, int len))
|
||||
{
|
||||
int i;
|
||||
|
||||
writeChar('[', ctx, writeBuf, writeFunc);
|
||||
|
||||
for (i = 0; i < val->u.array.len; ++i)
|
||||
{
|
||||
writeValue(&val->u.array.values[i], ctx, writeBuf, writeFunc);
|
||||
if (i != val->u.array.len - 1)
|
||||
writeChar(',', ctx, writeBuf, writeFunc);
|
||||
}
|
||||
|
||||
writeChar(']', ctx, writeBuf, writeFunc);
|
||||
}
|
||||
|
||||
static void writeTrue(void *ctx, char *writeBuf, int (*writeFunc)(void *ctx, void *buf, int len))
|
||||
{
|
||||
writeString("true", ctx, writeBuf, writeFunc);
|
||||
}
|
||||
|
||||
static void writeFalse(void *ctx, char *writeBuf, int (*writeFunc)(void *ctx, void *buf, int len))
|
||||
{
|
||||
writeString("false", ctx, writeBuf, writeFunc);
|
||||
}
|
||||
|
||||
static void writeNull(void *ctx, char *writeBuf, int (*writeFunc)(void *ctx, void *buf, int len))
|
||||
{
|
||||
writeString("null", ctx, writeBuf, writeFunc);
|
||||
}
|
||||
|
||||
static void writeValue(
|
||||
const struct JsonVal *val,
|
||||
void *ctx, char *writeBuf,
|
||||
int (*writeFunc)(void *ctx, void *buf, int len))
|
||||
{
|
||||
switch (val->type)
|
||||
{
|
||||
case jsonStringT:
|
||||
writeQuotedString(val->u.string, ctx, writeBuf, writeFunc);
|
||||
break;
|
||||
case jsonNumberT:
|
||||
writeNumber(val->u.number, ctx, writeBuf, writeFunc);
|
||||
break;
|
||||
case jsonObjectT:
|
||||
writeObject(val, ctx, writeBuf, writeFunc);
|
||||
break;
|
||||
case jsonArrayT:
|
||||
writeArray(val, ctx, writeBuf, writeFunc);
|
||||
break;
|
||||
case jsonTrueT:
|
||||
writeTrue(ctx, writeBuf, writeFunc);
|
||||
break;
|
||||
case jsonFalseT:
|
||||
writeFalse(ctx, writeBuf, writeFunc);
|
||||
break;
|
||||
case jsonNullT:
|
||||
writeNull(ctx, writeBuf, writeFunc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void writeObject(
|
||||
const struct JsonVal *val,
|
||||
void *ctx, char *writeBuf,
|
||||
int (*writeFunc)(void *ctx, void *buf, int len))
|
||||
{
|
||||
int i;
|
||||
|
||||
writeChar('{', ctx, writeBuf, writeFunc);
|
||||
|
||||
for (i = 0; i < val->u.object.len; ++i)
|
||||
{
|
||||
writeQuotedString(val->u.object.keys[i], ctx, writeBuf, writeFunc);
|
||||
writeChar(':', ctx, writeBuf, writeFunc);
|
||||
writeValue(&val->u.object.values[i], ctx, writeBuf, writeFunc);
|
||||
|
||||
if (i != val->u.object.len - 1)
|
||||
writeChar(',', ctx, writeBuf, writeFunc);
|
||||
}
|
||||
|
||||
writeChar('}', ctx, writeBuf, writeFunc);
|
||||
}
|
||||
|
||||
struct CountingContext
|
||||
{
|
||||
void *userCtx;
|
||||
int (*userWriteFunc)(void *ctx, void *buf, int len);
|
||||
int totalWrittenBytes;
|
||||
};
|
||||
|
||||
static int countingWriteWrapper(void *ctx, void *buf, int len)
|
||||
{
|
||||
struct CountingContext *countingCtx = (struct CountingContext *)ctx;
|
||||
countingCtx->totalWrittenBytes += len;
|
||||
|
||||
return countingCtx->userWriteFunc(countingCtx->userCtx, buf, len);
|
||||
}
|
||||
|
||||
int JsonVal_write(
|
||||
const struct JsonVal *val, void *ctx,
|
||||
int (*writeFunc)(void *ctx, void *buf, int len))
|
||||
{
|
||||
char writeBuf[kWriteBufSize];
|
||||
struct CountingContext countingCtx = {ctx, writeFunc, 0};
|
||||
|
||||
writeObject(val, &countingCtx, writeBuf, &countingWriteWrapper);
|
||||
writeBuf[0] = '\0';
|
||||
countingWriteWrapper(&countingCtx, writeBuf, 1);
|
||||
|
||||
return countingCtx.totalWrittenBytes;
|
||||
}
|
||||
|
||||
int JsonVal_writeString(const struct JsonVal *val, char *buf, int len)
|
||||
{
|
||||
struct Buf ctx = {buf, len};
|
||||
return JsonVal_write(val, &ctx, &writeToBuf);
|
||||
}
|
||||
|
||||
void JsonVal_destroy(struct JsonVal *val)
|
||||
{
|
||||
int i;
|
||||
|
||||
switch (val->type)
|
||||
{
|
||||
case jsonStringT:
|
||||
free(val->u.string);
|
||||
break;
|
||||
case jsonObjectT:
|
||||
for (i = 0; i < val->u.object.len; ++i)
|
||||
{
|
||||
JsonVal_destroy(&val->u.object.values[i]);
|
||||
free(val->u.object.keys[i]);
|
||||
}
|
||||
free(val->u.object.values);
|
||||
free(val->u.object.keys);
|
||||
break;
|
||||
case jsonArrayT:
|
||||
for (i = 0; i < val->u.array.len; ++i)
|
||||
JsonVal_destroy(&val->u.array.values[i]);
|
||||
free(val->u.array.values);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
94
json.h
Normal file
94
json.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
enum JsonType
|
||||
{
|
||||
jsonStringT,
|
||||
jsonNumberT,
|
||||
jsonObjectT,
|
||||
jsonArrayT,
|
||||
jsonTrueT,
|
||||
jsonFalseT,
|
||||
jsonNullT,
|
||||
};
|
||||
|
||||
struct JsonVal
|
||||
{
|
||||
enum JsonType type;
|
||||
union
|
||||
{
|
||||
char *string;
|
||||
long double number;
|
||||
struct {
|
||||
char **keys;
|
||||
struct JsonVal *values;
|
||||
int len;
|
||||
} object;
|
||||
struct {
|
||||
struct JsonVal *values;
|
||||
int len;
|
||||
} array;
|
||||
} u;
|
||||
};
|
||||
|
||||
|
||||
struct JsonVal jsonParseString(const char *str, char *errorBuf, int errorBufSize);
|
||||
|
||||
/*creates empty (top-level) object*/
|
||||
struct JsonVal jsonCreateObject();
|
||||
|
||||
/*check type functions*/
|
||||
int JsonVal_isString(const struct JsonVal *val);
|
||||
int JsonVal_isNumber(const struct JsonVal *val);
|
||||
int JsonVal_isObject(const struct JsonVal *val);
|
||||
int JsonVal_isArray(const struct JsonVal *val);
|
||||
int JsonVal_isTrue(const struct JsonVal *val);
|
||||
int JsonVal_isFalse(const struct JsonVal *val);
|
||||
int JsonVal_isNull(const struct JsonVal *val);
|
||||
|
||||
/*add subvalues to existing values. return pointers to the newly created values.*/
|
||||
struct JsonVal *JsonVal_objectAddString(struct JsonVal *val, const char *key, const char *value);
|
||||
struct JsonVal *JsonVal_objectAddNumber(struct JsonVal *val, const char *key, long double number);
|
||||
struct JsonVal *JsonVal_objectAddObject(struct JsonVal *val, const char *key);
|
||||
struct JsonVal *JsonVal_objectAddArray(struct JsonVal *val, const char *key);
|
||||
struct JsonVal *JsonVal_objectAddTrue(struct JsonVal *val, const char *key);
|
||||
struct JsonVal *JsonVal_objectAddFalse(struct JsonVal *val, const char *key);
|
||||
struct JsonVal *JsonVal_objectAddNull(struct JsonVal *val, const char *key);
|
||||
|
||||
struct JsonVal *JsonVal_arrayAddString(struct JsonVal *val, const char *value);
|
||||
struct JsonVal *JsonVal_arrayAddNumber(struct JsonVal *val, long double number);
|
||||
struct JsonVal *JsonVal_arrayAddObject(struct JsonVal *val);
|
||||
struct JsonVal *JsonVal_arrayAddArray(struct JsonVal *val);
|
||||
struct JsonVal *JsonVal_arrayAddTrue(struct JsonVal *val);
|
||||
struct JsonVal *JsonVal_arrayAddFalse(struct JsonVal *val);
|
||||
struct JsonVal *JsonVal_arrayAddNull(struct JsonVal *val);
|
||||
|
||||
/*access functions*/
|
||||
int JsonVal_arrayLen(struct JsonVal *val);
|
||||
struct JsonVal *JsonVal_arrayAt(struct JsonVal* val, int index);
|
||||
|
||||
struct JsonVal *JsonVal_getObjectValueByKey(const struct JsonVal *val, const char *key);
|
||||
|
||||
void JsonVal_forEachArrayElement(
|
||||
const struct JsonVal *val,
|
||||
void *ctx,
|
||||
void (*action)(void *, const struct JsonVal *));
|
||||
|
||||
void JsonVal_forEachObjectElement(
|
||||
const struct JsonVal *val,
|
||||
void *ctx,
|
||||
void (*action)(void *, const char *key, const struct JsonVal *));
|
||||
|
||||
/*write functions*/
|
||||
int JsonVal_write(
|
||||
const struct JsonVal *val,
|
||||
/*User context. Will be passed to the writeFunc callback.*/
|
||||
void *ctx,
|
||||
/*user supplied write callback function.*/
|
||||
int (*writeFunc)(void *ctx, void *buf, int len));
|
||||
|
||||
/*returns total bytes required*/
|
||||
int JsonVal_writeString(const struct JsonVal *val, char *buf, int len);
|
||||
|
||||
void JsonVal_destroy(struct JsonVal *val);
|
||||
279
l2cap.h
Normal file
279
l2cap.h
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2000-2001 Qualcomm Incorporated
|
||||
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
* Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
|
||||
* Copyright (c) 2012 Code Aurora Forum. 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 as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __L2CAP_H
|
||||
#define __L2CAP_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
/* L2CAP defaults */
|
||||
#define L2CAP_DEFAULT_MTU 672
|
||||
#define L2CAP_DEFAULT_FLUSH_TO 0xFFFF
|
||||
|
||||
/* L2CAP socket address */
|
||||
struct sockaddr_l2 {
|
||||
sa_family_t l2_family;
|
||||
unsigned short l2_psm;
|
||||
bdaddr_t l2_bdaddr;
|
||||
unsigned short l2_cid;
|
||||
uint8_t l2_bdaddr_type;
|
||||
};
|
||||
|
||||
/* L2CAP socket options */
|
||||
#define L2CAP_OPTIONS 0x01
|
||||
struct l2cap_options {
|
||||
uint16_t omtu;
|
||||
uint16_t imtu;
|
||||
uint16_t flush_to;
|
||||
uint8_t mode;
|
||||
uint8_t fcs;
|
||||
uint8_t max_tx;
|
||||
uint16_t txwin_size;
|
||||
};
|
||||
|
||||
#define L2CAP_CONNINFO 0x02
|
||||
struct l2cap_conninfo {
|
||||
uint16_t hci_handle;
|
||||
uint8_t dev_class[3];
|
||||
};
|
||||
|
||||
#define L2CAP_LM 0x03
|
||||
#define L2CAP_LM_MASTER 0x0001
|
||||
#define L2CAP_LM_AUTH 0x0002
|
||||
#define L2CAP_LM_ENCRYPT 0x0004
|
||||
#define L2CAP_LM_TRUSTED 0x0008
|
||||
#define L2CAP_LM_RELIABLE 0x0010
|
||||
#define L2CAP_LM_SECURE 0x0020
|
||||
|
||||
/* L2CAP command codes */
|
||||
#define L2CAP_COMMAND_REJ 0x01
|
||||
#define L2CAP_CONN_REQ 0x02
|
||||
#define L2CAP_CONN_RSP 0x03
|
||||
#define L2CAP_CONF_REQ 0x04
|
||||
#define L2CAP_CONF_RSP 0x05
|
||||
#define L2CAP_DISCONN_REQ 0x06
|
||||
#define L2CAP_DISCONN_RSP 0x07
|
||||
#define L2CAP_ECHO_REQ 0x08
|
||||
#define L2CAP_ECHO_RSP 0x09
|
||||
#define L2CAP_INFO_REQ 0x0a
|
||||
#define L2CAP_INFO_RSP 0x0b
|
||||
#define L2CAP_CREATE_REQ 0x0c
|
||||
#define L2CAP_CREATE_RSP 0x0d
|
||||
#define L2CAP_MOVE_REQ 0x0e
|
||||
#define L2CAP_MOVE_RSP 0x0f
|
||||
#define L2CAP_MOVE_CFM 0x10
|
||||
#define L2CAP_MOVE_CFM_RSP 0x11
|
||||
|
||||
/* L2CAP extended feature mask */
|
||||
#define L2CAP_FEAT_FLOWCTL 0x00000001
|
||||
#define L2CAP_FEAT_RETRANS 0x00000002
|
||||
#define L2CAP_FEAT_BIDIR_QOS 0x00000004
|
||||
#define L2CAP_FEAT_ERTM 0x00000008
|
||||
#define L2CAP_FEAT_STREAMING 0x00000010
|
||||
#define L2CAP_FEAT_FCS 0x00000020
|
||||
#define L2CAP_FEAT_EXT_FLOW 0x00000040
|
||||
#define L2CAP_FEAT_FIXED_CHAN 0x00000080
|
||||
#define L2CAP_FEAT_EXT_WINDOW 0x00000100
|
||||
#define L2CAP_FEAT_UCD 0x00000200
|
||||
|
||||
/* L2CAP fixed channels */
|
||||
#define L2CAP_FC_L2CAP 0x02
|
||||
#define L2CAP_FC_CONNLESS 0x04
|
||||
#define L2CAP_FC_A2MP 0x08
|
||||
|
||||
/* L2CAP structures */
|
||||
typedef struct {
|
||||
uint16_t len;
|
||||
uint16_t cid;
|
||||
} __attribute__ ((packed)) l2cap_hdr;
|
||||
#define L2CAP_HDR_SIZE 4
|
||||
|
||||
typedef struct {
|
||||
uint8_t code;
|
||||
uint8_t ident;
|
||||
uint16_t len;
|
||||
} __attribute__ ((packed)) l2cap_cmd_hdr;
|
||||
#define L2CAP_CMD_HDR_SIZE 4
|
||||
|
||||
typedef struct {
|
||||
uint16_t reason;
|
||||
} __attribute__ ((packed)) l2cap_cmd_rej;
|
||||
#define L2CAP_CMD_REJ_SIZE 2
|
||||
|
||||
typedef struct {
|
||||
uint16_t psm;
|
||||
uint16_t scid;
|
||||
} __attribute__ ((packed)) l2cap_conn_req;
|
||||
#define L2CAP_CONN_REQ_SIZE 4
|
||||
|
||||
typedef struct {
|
||||
uint16_t dcid;
|
||||
uint16_t scid;
|
||||
uint16_t result;
|
||||
uint16_t status;
|
||||
} __attribute__ ((packed)) l2cap_conn_rsp;
|
||||
#define L2CAP_CONN_RSP_SIZE 8
|
||||
|
||||
/* connect result */
|
||||
#define L2CAP_CR_SUCCESS 0x0000
|
||||
#define L2CAP_CR_PEND 0x0001
|
||||
#define L2CAP_CR_BAD_PSM 0x0002
|
||||
#define L2CAP_CR_SEC_BLOCK 0x0003
|
||||
#define L2CAP_CR_NO_MEM 0x0004
|
||||
|
||||
/* connect status */
|
||||
#define L2CAP_CS_NO_INFO 0x0000
|
||||
#define L2CAP_CS_AUTHEN_PEND 0x0001
|
||||
#define L2CAP_CS_AUTHOR_PEND 0x0002
|
||||
|
||||
typedef struct {
|
||||
uint16_t dcid;
|
||||
uint16_t flags;
|
||||
uint8_t data[0];
|
||||
} __attribute__ ((packed)) l2cap_conf_req;
|
||||
#define L2CAP_CONF_REQ_SIZE 4
|
||||
|
||||
typedef struct {
|
||||
uint16_t scid;
|
||||
uint16_t flags;
|
||||
uint16_t result;
|
||||
uint8_t data[0];
|
||||
} __attribute__ ((packed)) l2cap_conf_rsp;
|
||||
#define L2CAP_CONF_RSP_SIZE 6
|
||||
|
||||
#define L2CAP_CONF_SUCCESS 0x0000
|
||||
#define L2CAP_CONF_UNACCEPT 0x0001
|
||||
#define L2CAP_CONF_REJECT 0x0002
|
||||
#define L2CAP_CONF_UNKNOWN 0x0003
|
||||
#define L2CAP_CONF_PENDING 0x0004
|
||||
#define L2CAP_CONF_EFS_REJECT 0x0005
|
||||
|
||||
typedef struct {
|
||||
uint8_t type;
|
||||
uint8_t len;
|
||||
uint8_t val[0];
|
||||
} __attribute__ ((packed)) l2cap_conf_opt;
|
||||
#define L2CAP_CONF_OPT_SIZE 2
|
||||
|
||||
#define L2CAP_CONF_MTU 0x01
|
||||
#define L2CAP_CONF_FLUSH_TO 0x02
|
||||
#define L2CAP_CONF_QOS 0x03
|
||||
#define L2CAP_CONF_RFC 0x04
|
||||
#define L2CAP_CONF_FCS 0x05
|
||||
#define L2CAP_CONF_EFS 0x06
|
||||
#define L2CAP_CONF_EWS 0x07
|
||||
|
||||
#define L2CAP_CONF_MAX_SIZE 22
|
||||
|
||||
#define L2CAP_MODE_BASIC 0x00
|
||||
#define L2CAP_MODE_RETRANS 0x01
|
||||
#define L2CAP_MODE_FLOWCTL 0x02
|
||||
#define L2CAP_MODE_ERTM 0x03
|
||||
#define L2CAP_MODE_STREAMING 0x04
|
||||
|
||||
#define L2CAP_SERVTYPE_NOTRAFFIC 0x00
|
||||
#define L2CAP_SERVTYPE_BESTEFFORT 0x01
|
||||
#define L2CAP_SERVTYPE_GUARANTEED 0x02
|
||||
|
||||
typedef struct {
|
||||
uint16_t dcid;
|
||||
uint16_t scid;
|
||||
} __attribute__ ((packed)) l2cap_disconn_req;
|
||||
#define L2CAP_DISCONN_REQ_SIZE 4
|
||||
|
||||
typedef struct {
|
||||
uint16_t dcid;
|
||||
uint16_t scid;
|
||||
} __attribute__ ((packed)) l2cap_disconn_rsp;
|
||||
#define L2CAP_DISCONN_RSP_SIZE 4
|
||||
|
||||
typedef struct {
|
||||
uint16_t type;
|
||||
} __attribute__ ((packed)) l2cap_info_req;
|
||||
#define L2CAP_INFO_REQ_SIZE 2
|
||||
|
||||
typedef struct {
|
||||
uint16_t type;
|
||||
uint16_t result;
|
||||
uint8_t data[0];
|
||||
} __attribute__ ((packed)) l2cap_info_rsp;
|
||||
#define L2CAP_INFO_RSP_SIZE 4
|
||||
|
||||
/* info type */
|
||||
#define L2CAP_IT_CL_MTU 0x0001
|
||||
#define L2CAP_IT_FEAT_MASK 0x0002
|
||||
|
||||
/* info result */
|
||||
#define L2CAP_IR_SUCCESS 0x0000
|
||||
#define L2CAP_IR_NOTSUPP 0x0001
|
||||
|
||||
typedef struct {
|
||||
uint16_t psm;
|
||||
uint16_t scid;
|
||||
uint8_t id;
|
||||
} __attribute__ ((packed)) l2cap_create_req;
|
||||
#define L2CAP_CREATE_REQ_SIZE 5
|
||||
|
||||
typedef struct {
|
||||
uint16_t dcid;
|
||||
uint16_t scid;
|
||||
uint16_t result;
|
||||
uint16_t status;
|
||||
} __attribute__ ((packed)) l2cap_create_rsp;
|
||||
#define L2CAP_CREATE_RSP_SIZE 8
|
||||
|
||||
typedef struct {
|
||||
uint16_t icid;
|
||||
uint8_t id;
|
||||
} __attribute__ ((packed)) l2cap_move_req;
|
||||
#define L2CAP_MOVE_REQ_SIZE 3
|
||||
|
||||
typedef struct {
|
||||
uint16_t icid;
|
||||
uint16_t result;
|
||||
} __attribute__ ((packed)) l2cap_move_rsp;
|
||||
#define L2CAP_MOVE_RSP_SIZE 4
|
||||
|
||||
typedef struct {
|
||||
uint16_t icid;
|
||||
uint16_t result;
|
||||
} __attribute__ ((packed)) l2cap_move_cfm;
|
||||
#define L2CAP_MOVE_CFM_SIZE 4
|
||||
|
||||
typedef struct {
|
||||
uint16_t icid;
|
||||
} __attribute__ ((packed)) l2cap_move_cfm_rsp;
|
||||
#define L2CAP_MOVE_CFM_RSP_SIZE 2
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __L2CAP_H */
|
||||
936
main.c
Normal file
936
main.c
Normal file
@@ -0,0 +1,936 @@
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include "mqtt.h"
|
||||
#include "client.h"
|
||||
#include <assert.h>
|
||||
#include "bluetooth.h"
|
||||
#include "hci.h"
|
||||
#include "hci_lib.h"
|
||||
#include "l2cap.h"
|
||||
#include "uuid.h"
|
||||
#include <sys/ioctl.h>
|
||||
#include "mainloop.h"
|
||||
#include "util.h"
|
||||
#include "att.h"
|
||||
#include "queue.h"
|
||||
#include "gatt-db.h"
|
||||
#include "gatt-client.h"
|
||||
#include "json.h"
|
||||
|
||||
#define ATT_CID 4
|
||||
#define PRLOG(...) \
|
||||
printf(__VA_ARGS__);
|
||||
|
||||
#define JSONOUTLEN 4096
|
||||
#define MQTT_SEND_PERIOD_S 10
|
||||
|
||||
static uint16_t mqtt_port = 1883;
|
||||
static uint16_t send_interval = MQTT_SEND_PERIOD_S;
|
||||
static bool verbose = false;
|
||||
static int handler_registered=0;
|
||||
struct client *cli;
|
||||
uint8_t rcv_buf[256];
|
||||
char jsonout[JSONOUTLEN];
|
||||
struct JsonVal top;
|
||||
uint8_t rcv_ptr=0, frame_len=0;
|
||||
uint16_t addr_base=0;
|
||||
char mqtt_buffer[BUFFER_SIZE_BYTES];
|
||||
client_t c;
|
||||
//int is_subscriber = 1;
|
||||
int keepalive_sec = 4;
|
||||
int nbytes;
|
||||
|
||||
struct client {
|
||||
/// socket
|
||||
int fd;
|
||||
/// pointer to a bt_att structure
|
||||
struct bt_att *att;
|
||||
/// pointer to a gatt_db structure
|
||||
struct gatt_db *db;
|
||||
/// pointer to a bt_gatt_client structure
|
||||
struct bt_gatt_client *gatt;
|
||||
/// session id
|
||||
unsigned int reliable_session_id;
|
||||
};
|
||||
|
||||
|
||||
|
||||
static void got_connection(client_t* psClnt)
|
||||
{
|
||||
uint8_t buf[128];
|
||||
require(psClnt != 0);
|
||||
if(!psClnt->sockfd) return;
|
||||
|
||||
printf("MQTT: connected to server.\n");
|
||||
nbytes = mqtt_encode_connect_msg2(buf, 0x02, keepalive_sec, (uint8_t*)"Solarlife", 9);
|
||||
|
||||
client_send(&c, (char*)buf, nbytes);
|
||||
}
|
||||
|
||||
static void lost_connection(client_t* psClnt)
|
||||
{
|
||||
require(psClnt != 0);
|
||||
printf("MQTT: disconnected from server.\n");
|
||||
mainloop_quit();
|
||||
}
|
||||
|
||||
static void got_data(client_t* psClnt, unsigned char* data, int nbytes)
|
||||
{
|
||||
require(psClnt != 0);
|
||||
|
||||
uint8_t ctrl = data[0] >> 4;
|
||||
if (ctrl == CTRL_PUBACK) {
|
||||
printf("MQTT server acknowledged data\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int16_t b2int (uint8_t *ptr) {
|
||||
|
||||
return ptr[1] | ptr[0] << 8;
|
||||
|
||||
}
|
||||
|
||||
|
||||
int b2int2 (uint8_t *ptr) {
|
||||
|
||||
return ptr[0] | ptr[1] << 8;
|
||||
|
||||
}
|
||||
|
||||
|
||||
uint16_t ModRTU_CRC(uint8_t* buf, int len)
|
||||
{
|
||||
uint16_t crc = 0xFFFF;
|
||||
|
||||
for (int pos = 0; pos < len; pos++) {
|
||||
crc ^= (uint16_t)buf[pos]; // XOR byte into least sig. byte of crc
|
||||
|
||||
for (int i = 8; i != 0; i--) { // Loop over each bit
|
||||
if ((crc & 0x0001) != 0) { // If the LSB is set
|
||||
crc >>= 1; // Shift right and XOR 0xA001
|
||||
crc ^= 0xA001;
|
||||
}
|
||||
else // Else LSB is not set
|
||||
crc >>= 1; // Just shift right
|
||||
}
|
||||
}
|
||||
// Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)
|
||||
return crc;
|
||||
}
|
||||
|
||||
|
||||
static void write_cb(bool success, uint8_t att_ecode, void *user_data)
|
||||
{
|
||||
/*
|
||||
if (success) {
|
||||
PRLOG("\nWrite successful\n");
|
||||
} else {
|
||||
PRLOG("\nWrite failed: %s (0x%02x)\n",
|
||||
ecode_to_string(att_ecode), att_ecode);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
int send_modbus_req(uint8_t eq_id, uint8_t fct_code,uint16_t addr, uint16_t len) {
|
||||
|
||||
uint8_t buf[10];
|
||||
|
||||
|
||||
buf[0]=eq_id;
|
||||
buf[1]=fct_code;
|
||||
buf[2]=addr>>8;
|
||||
buf[3]=addr&0xff;
|
||||
buf[4]=len>>8;
|
||||
buf[5]=len&0xff;
|
||||
uint16_t crc = ModRTU_CRC(buf,6);
|
||||
buf[7]=crc>>8;
|
||||
buf[6]=crc&0xff;
|
||||
addr_base = addr;
|
||||
|
||||
rcv_ptr = 0; //reset pointer
|
||||
top = jsonCreateObject();
|
||||
|
||||
if (!bt_gatt_client_write_value(cli->gatt, 0x14, buf, 8, write_cb, NULL, NULL)) {
|
||||
printf("Failed to initiate write procedure\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void log_service_event(struct gatt_db_attribute *attr, const char *str)
|
||||
{
|
||||
char uuid_str[MAX_LEN_UUID_STR];
|
||||
bt_uuid_t uuid;
|
||||
uint16_t start, end;
|
||||
|
||||
gatt_db_attribute_get_service_uuid(attr, &uuid);
|
||||
bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
|
||||
|
||||
gatt_db_attribute_get_service_handles(attr, &start, &end);
|
||||
|
||||
PRLOG("%s - UUID: %s start: 0x%04x end: 0x%04x\n", str, uuid_str,
|
||||
start, end);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void register_notify_cb(uint16_t att_ecode, void *user_data)
|
||||
{
|
||||
if (att_ecode) {
|
||||
PRLOG("Failed to register notify handler "
|
||||
"- error code: 0x%02x\n", att_ecode);
|
||||
return;
|
||||
}
|
||||
|
||||
PRLOG("Registered notify handler!\n");
|
||||
handler_registered = 1;
|
||||
}
|
||||
|
||||
void parse_val(uint8_t *buf, uint16_t addr) {
|
||||
|
||||
//printf("Parsing value at address %04x\n",addr);
|
||||
uint8_t ptr = addr*2+3;
|
||||
addr+=addr_base;
|
||||
|
||||
switch (addr) {
|
||||
|
||||
case 0x3000:
|
||||
JsonVal_objectAddNumber(&top, "PV_rated_voltage", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3001:
|
||||
JsonVal_objectAddNumber(&top, "PV_rated_current", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3002:
|
||||
JsonVal_objectAddNumber(&top, "PV_rated_power_l", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3003:
|
||||
JsonVal_objectAddNumber(&top, "PV_rated_power_h", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
|
||||
case 0x3004:
|
||||
JsonVal_objectAddNumber(&top, "battery_rated_voltage", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3005:
|
||||
JsonVal_objectAddNumber(&top, "battery_rated_current", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3006:
|
||||
JsonVal_objectAddNumber(&top, "battery_rated_power_l", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3007:
|
||||
JsonVal_objectAddNumber(&top, "battery_rated_power_h", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
|
||||
case 0x3008:
|
||||
JsonVal_objectAddNumber(&top, "load_rated_voltage", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3009:
|
||||
JsonVal_objectAddNumber(&top, "load_rated_current", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x300a:
|
||||
JsonVal_objectAddNumber(&top, "load_rated_power_l", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x300b:
|
||||
JsonVal_objectAddNumber(&top, "load_rated_power_h", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
|
||||
|
||||
case 0x3030:
|
||||
JsonVal_objectAddNumber(&top, "slave_id", (int)b2int(buf+ptr));
|
||||
break;
|
||||
case 0x3031:
|
||||
JsonVal_objectAddNumber(&top, "running_days", (int)b2int(buf+ptr));
|
||||
break;
|
||||
case 0x3032:
|
||||
JsonVal_objectAddNumber(&top, "sys_voltage", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3033:
|
||||
JsonVal_objectAddNumber(&top, "battery_status", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x3034:
|
||||
JsonVal_objectAddNumber(&top, "charge_status", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x3035:
|
||||
JsonVal_objectAddNumber(&top, "discharge_status", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x3036:
|
||||
JsonVal_objectAddNumber(&top, "env_temperature", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3037:
|
||||
JsonVal_objectAddNumber(&top, "sys_temperature", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3038:
|
||||
JsonVal_objectAddNumber(&top, "undervoltage_times", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x3039:
|
||||
JsonVal_objectAddNumber(&top, "fullycharged_times", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x303a:
|
||||
JsonVal_objectAddNumber(&top, "overvoltage_prot_times", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x303b:
|
||||
JsonVal_objectAddNumber(&top, "overcurrent_prot_times", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x303c:
|
||||
JsonVal_objectAddNumber(&top, "shortcircuit_prot_times", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x303d:
|
||||
JsonVal_objectAddNumber(&top, "opencircuit_prot_times", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x303e:
|
||||
JsonVal_objectAddNumber(&top, "hw_prot_times", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x303f:
|
||||
JsonVal_objectAddNumber(&top, "charge_overtemp_prot_times", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x3040:
|
||||
JsonVal_objectAddNumber(&top, "discharge_overtemp_prot_times", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x3045:
|
||||
JsonVal_objectAddNumber(&top, "battery_remaining_capacity", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x3046:
|
||||
JsonVal_objectAddNumber(&top, "battery_voltage", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3047:
|
||||
JsonVal_objectAddNumber(&top, "battery_current", (float)b2int(buf+ptr)/100);
|
||||
//JsonVal_objectAddNumber(&top, "battery_current", b2int(buf+ptr)/100);
|
||||
//printf("current: %d\n",b2int(buf+ptr));
|
||||
|
||||
break;
|
||||
case 0x3048:
|
||||
JsonVal_objectAddNumber(&top, "battery_power_lo", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3049:
|
||||
JsonVal_objectAddNumber(&top, "battery_power_hi", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x304a:
|
||||
JsonVal_objectAddNumber(&top, "load_voltage", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x304b:
|
||||
JsonVal_objectAddNumber(&top, "load_current", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x304c:
|
||||
JsonVal_objectAddNumber(&top, "load_power_l", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x304d:
|
||||
JsonVal_objectAddNumber(&top, "load_power_h", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x304e:
|
||||
JsonVal_objectAddNumber(&top, "solar_voltage", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x304f:
|
||||
JsonVal_objectAddNumber(&top, "solar_current", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3050:
|
||||
JsonVal_objectAddNumber(&top, "solar_power_l", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3051:
|
||||
JsonVal_objectAddNumber(&top, "solar_power_h", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3052:
|
||||
JsonVal_objectAddNumber(&top, "daily_production", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3053:
|
||||
JsonVal_objectAddNumber(&top, "total_production_l", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3054:
|
||||
JsonVal_objectAddNumber(&top, "total_production_h", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3055:
|
||||
JsonVal_objectAddNumber(&top, "daily_consumption", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3056:
|
||||
JsonVal_objectAddNumber(&top, "total_consumption_l", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3057:
|
||||
JsonVal_objectAddNumber(&top, "total_consumption_h", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3058:
|
||||
JsonVal_objectAddNumber(&top, "lighttime_daily", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x305d:
|
||||
JsonVal_objectAddNumber(&top, "monthly_production_l", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x305e:
|
||||
JsonVal_objectAddNumber(&top, "monthly_production_h", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x305f:
|
||||
JsonVal_objectAddNumber(&top, "yearly_production_l", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x3060:
|
||||
JsonVal_objectAddNumber(&top, "yearly_production_h", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
|
||||
case 0x9017:
|
||||
JsonVal_objectAddNumber(&top, "rtc_second", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x9018:
|
||||
JsonVal_objectAddNumber(&top, "rtc_minute", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x9019:
|
||||
JsonVal_objectAddNumber(&top, "rtc_hour", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x901a:
|
||||
JsonVal_objectAddNumber(&top, "rtc_day", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x901b:
|
||||
JsonVal_objectAddNumber(&top, "rtc_month", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x901c:
|
||||
JsonVal_objectAddNumber(&top, "rtc_year", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x901d:
|
||||
JsonVal_objectAddNumber(&top, "baud_rate", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x901f:
|
||||
JsonVal_objectAddNumber(&top, "password", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x9021:
|
||||
JsonVal_objectAddNumber(&top, "battery_type", b2int(buf+ptr));
|
||||
break;
|
||||
case 0x9022:
|
||||
JsonVal_objectAddNumber(&top, "lvp_voltage", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x9023:
|
||||
JsonVal_objectAddNumber(&top, "lvr_voltage", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x9024:
|
||||
JsonVal_objectAddNumber(&top, "boost_voltage", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x9025:
|
||||
JsonVal_objectAddNumber(&top, "equalizing_voltage", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
case 0x9026:
|
||||
JsonVal_objectAddNumber(&top, "floating_voltage", (float)b2int(buf+ptr)/100);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void notify_cb(uint16_t value_handle, const uint8_t *value,
|
||||
uint16_t length, void *user_data)
|
||||
{
|
||||
|
||||
memcpy(rcv_buf+rcv_ptr, value, length);
|
||||
rcv_ptr+=length;
|
||||
|
||||
if (rcv_ptr==rcv_buf[2]+5) {
|
||||
|
||||
uint16_t crc_r = b2int2(rcv_buf+rcv_ptr-2);
|
||||
uint16_t crc = ModRTU_CRC(rcv_buf, rcv_ptr-2);
|
||||
if((crc==crc_r) && addr_base) { //CRC check passed
|
||||
for (int ii = 0; ii<(rcv_buf[2]-2)/2; ii++) {
|
||||
parse_val(rcv_buf,ii);
|
||||
|
||||
}
|
||||
time_t now;
|
||||
time(&now);
|
||||
char buf[sizeof "2011-10-08T07:07:09Z"];
|
||||
strftime(buf, sizeof buf, "%FT%TZ", gmtime(&now));
|
||||
JsonVal_objectAddString(&top, "timestamp", buf);
|
||||
JsonVal_writeString(&top, jsonout, JSONOUTLEN);
|
||||
puts(jsonout);
|
||||
char buf2[8000];
|
||||
nbytes = mqtt_encode_publish_msg((uint8_t*)buf2, (uint8_t*)"Solarlife",9 , 1, 10, (uint8_t*)jsonout, strlen(jsonout));
|
||||
if(c.sockfd) {
|
||||
client_send(&c, buf2, nbytes);
|
||||
client_poll(&c, 1000000);
|
||||
}
|
||||
JsonVal_destroy(&top);
|
||||
//if(addr_base<0x9000) {
|
||||
// send_modbus_req(0x01, 0x03, 0x9000, 0x40);
|
||||
// addr_base = 0;
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void ready_cb(bool success, uint8_t att_ecode, void *user_data)
|
||||
{
|
||||
struct client *cli = user_data;
|
||||
|
||||
if (!success) {
|
||||
PRLOG("GATT discovery procedures failed - error code: 0x%02x\n",
|
||||
att_ecode);
|
||||
return;
|
||||
}
|
||||
|
||||
PRLOG("GATT discovery procedures complete\n");
|
||||
|
||||
unsigned char buf[] = {01,00};
|
||||
if (!bt_gatt_client_write_value(cli->gatt, 0x12, buf, sizeof(buf),
|
||||
write_cb,
|
||||
NULL, NULL))
|
||||
printf("Failed to initiate write procedure\n");
|
||||
|
||||
unsigned int id = bt_gatt_client_register_notify(cli->gatt, 0x11,
|
||||
register_notify_cb,
|
||||
notify_cb, NULL, NULL);
|
||||
if (!id) {
|
||||
printf("Failed to register notify handler\n");
|
||||
}
|
||||
|
||||
PRLOG("Registering notify handler with id: %u\n", id);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void service_removed_cb(struct gatt_db_attribute *attr, void *user_data)
|
||||
{
|
||||
log_service_event(attr, "Service Removed");
|
||||
}
|
||||
|
||||
|
||||
static void service_added_cb(struct gatt_db_attribute *attr, void *user_data)
|
||||
{
|
||||
log_service_event(attr, "Service Added");
|
||||
}
|
||||
|
||||
|
||||
static void att_disconnect_cb(int err, void *user_data)
|
||||
{
|
||||
printf("Device disconnected: %s\n", strerror(err));
|
||||
mainloop_quit();
|
||||
}
|
||||
|
||||
void send_mqtt_ping(void) {
|
||||
|
||||
unsigned char buf[100];
|
||||
nbytes = mqtt_encode_ping_msg(buf);
|
||||
if ((nbytes != 0) && (c.sockfd))
|
||||
{
|
||||
client_send(&c, (char*)buf, nbytes);
|
||||
client_poll(&c, 0);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void signal_cb(int signum, void *user_data)
|
||||
{
|
||||
|
||||
static int sec_counter;
|
||||
switch (signum) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
mainloop_quit();
|
||||
break;
|
||||
case SIGALRM:
|
||||
{
|
||||
//if(addr_base) send_modbus_req(0x01, 0x03, 0x9000, 0x40);
|
||||
//else {
|
||||
// send_modbus_req(0x01, 0x04, 0x3000, 0x7c);
|
||||
// addr_base=0;
|
||||
//}
|
||||
//uint16_t len = form_modbus_msg(buff,0x01, 0x03, 0x9000, 0x40);
|
||||
//send_modbus_req(0x01, 0x04, 0x3000, 0x7c);
|
||||
//send_modbus_req(0x01, 0x04, 0x3047, 0x20);
|
||||
send_mqtt_ping();
|
||||
if(handler_registered) {
|
||||
if (++sec_counter>send_interval) {
|
||||
send_modbus_req(0x01, 0x04, 0x3000, 0x7c);
|
||||
sec_counter=0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
alarm(1);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void client_destroy(struct client *cli)
|
||||
{
|
||||
bt_gatt_client_unref(cli->gatt);
|
||||
bt_att_unref(cli->att);
|
||||
free(cli);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct client *client_create(int fd, uint16_t mtu)
|
||||
{
|
||||
struct client *cli;
|
||||
|
||||
cli = new0(struct client, 1);
|
||||
if (!cli) {
|
||||
fprintf(stderr, "Failed to allocate memory for client\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cli->att = bt_att_new(fd, false);
|
||||
if (!cli->att) {
|
||||
fprintf(stderr, "Failed to initialze ATT transport layer\n");
|
||||
bt_att_unref(cli->att);
|
||||
free(cli);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!bt_att_set_close_on_unref(cli->att, true)) {
|
||||
fprintf(stderr, "Failed to set up ATT transport layer\n");
|
||||
bt_att_unref(cli->att);
|
||||
free(cli);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!bt_att_register_disconnect(cli->att, att_disconnect_cb, NULL,
|
||||
NULL)) {
|
||||
fprintf(stderr, "Failed to set ATT disconnect handler\n");
|
||||
bt_att_unref(cli->att);
|
||||
free(cli);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cli->fd = fd;
|
||||
cli->db = gatt_db_new();
|
||||
if (!cli->db) {
|
||||
fprintf(stderr, "Failed to create GATT database\n");
|
||||
bt_att_unref(cli->att);
|
||||
free(cli);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu);
|
||||
if (!cli->gatt) {
|
||||
fprintf(stderr, "Failed to create GATT client\n");
|
||||
gatt_db_unref(cli->db);
|
||||
bt_att_unref(cli->att);
|
||||
free(cli);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gatt_db_register(cli->db, service_added_cb, service_removed_cb,
|
||||
NULL, NULL);
|
||||
|
||||
bt_gatt_client_set_ready_handler(cli->gatt, ready_cb, cli, NULL);
|
||||
|
||||
/* bt_gatt_client already holds a reference */
|
||||
gatt_db_unref(cli->db);
|
||||
|
||||
return cli;
|
||||
}
|
||||
|
||||
|
||||
static int l2cap_le_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
|
||||
int sec)
|
||||
{
|
||||
int sock;
|
||||
struct sockaddr_l2 srcaddr, dstaddr;
|
||||
struct bt_security btsec;
|
||||
|
||||
|
||||
sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
|
||||
if (sock < 0) {
|
||||
perror("Failed to create L2CAP socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set up source address */
|
||||
memset(&srcaddr, 0, sizeof(srcaddr));
|
||||
srcaddr.l2_family = AF_BLUETOOTH;
|
||||
srcaddr.l2_cid = htobs(ATT_CID);
|
||||
srcaddr.l2_bdaddr_type = 0;
|
||||
bacpy(&srcaddr.l2_bdaddr, src);
|
||||
|
||||
if (bind(sock, (struct sockaddr *)&srcaddr, sizeof(srcaddr)) < 0) {
|
||||
perror("Failed to bind L2CAP socket");
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set the security level */
|
||||
memset(&btsec, 0, sizeof(btsec));
|
||||
btsec.level = sec;
|
||||
if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &btsec,
|
||||
sizeof(btsec)) != 0) {
|
||||
fprintf(stderr, "Failed to set L2CAP security level\n");
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set up destination address */
|
||||
memset(&dstaddr, 0, sizeof(dstaddr));
|
||||
dstaddr.l2_family = AF_BLUETOOTH;
|
||||
dstaddr.l2_cid = htobs(ATT_CID);
|
||||
dstaddr.l2_bdaddr_type = dst_type;
|
||||
bacpy(&dstaddr.l2_bdaddr, dst);
|
||||
|
||||
printf("Connecting to BT device...");
|
||||
fflush(stdout);
|
||||
|
||||
if (connect(sock, (struct sockaddr *) &dstaddr, sizeof(dstaddr)) < 0) {
|
||||
perror(" Failed to connect to BT device");
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf(" Done\n");
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
void inthandler(int dummy)
|
||||
{
|
||||
(void)dummy;
|
||||
|
||||
uint8_t buf[128];
|
||||
int nbytes = mqtt_encode_disconnect_msg(buf);
|
||||
if (nbytes != 0)
|
||||
{
|
||||
client_send(&c, (char*)buf, nbytes);
|
||||
}
|
||||
|
||||
client_poll(&c, 10000000);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void usage(char *argv[])
|
||||
{
|
||||
printf("%s\n",argv[0]);
|
||||
printf("Usage:\n\t%s [options]\n",argv[0]);
|
||||
|
||||
printf("Options:\n"
|
||||
"\t-i, --index <id>\t\tSpecify adapter index, e.g. hci0\n"
|
||||
"\t-d, --dest <addr>\t\tSpecify the destination mac address of the Solarlife device\n"
|
||||
"\t-a, --host <ipaddr>\t\tSpecify the MQTT broker address\n"
|
||||
"\t-p, --port \t\t\tMQTT broker port, default: %d\n"
|
||||
"\t-t, --interval \t\t\tSet data sending interval in seconds, default: %d s\n"
|
||||
"\t-v, --verbose\t\t\tEnable extra logging\n"
|
||||
"\t-h, --help\t\t\tDisplay help\n",mqtt_port, send_interval);
|
||||
|
||||
printf("Example:\n"
|
||||
"%s -d C4:BE:84:70:29:04 -a test.mosquitto.org -t 10\n",argv[0]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct option main_options[] = {
|
||||
{ "index", 1, 0, 'i' },
|
||||
{ "dest", 1, 0, 'd' },
|
||||
{ "host", 1, 0, 'a' },
|
||||
{ "port", 1, 0, 'p' },
|
||||
{ "interval", 1, 0, 't' },
|
||||
{ "verbose", 0, 0, 'v' },
|
||||
{ "help", 0, 0, 'h' },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
int opt;
|
||||
int sec = BT_SECURITY_LOW;
|
||||
uint16_t mtu = 0;
|
||||
uint8_t dst_type = BDADDR_LE_PUBLIC;
|
||||
bool dst_addr_given = false;
|
||||
bdaddr_t src_addr, dst_addr;
|
||||
int dev_id = -1;
|
||||
int fd;
|
||||
sigset_t mask;
|
||||
char mqtt_host[100];
|
||||
|
||||
|
||||
//str2ba("04:7f:0e:54:61:0c", &dst_addr);
|
||||
|
||||
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "+hva:t:d:i:p:",
|
||||
main_options, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
usage(argv);
|
||||
return EXIT_SUCCESS;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
case 'a':
|
||||
strncpy(mqtt_host,optarg,sizeof(mqtt_host));
|
||||
break;
|
||||
|
||||
case 'p': {
|
||||
int arg;
|
||||
|
||||
arg = atoi(optarg);
|
||||
if (arg <= 0) {
|
||||
fprintf(stderr, "Invalid MQTT port: %d\n", arg);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (arg > UINT16_MAX) {
|
||||
fprintf(stderr, "MQTT port too large: %d\n", arg);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
mqtt_port = (uint16_t)arg;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case 't': {
|
||||
int arg;
|
||||
|
||||
arg = atoi(optarg);
|
||||
if (arg <= 0) {
|
||||
fprintf(stderr, "Invalid interval: %d s\n", arg);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (arg > UINT16_MAX) {
|
||||
fprintf(stderr, "Interval too large: %d s\n", arg);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (arg < 1) {
|
||||
fprintf(stderr, "Interval too small: %d s\n", arg);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
send_interval = (uint16_t)arg;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'd':
|
||||
if (str2ba(optarg, &dst_addr) < 0) {
|
||||
fprintf(stderr, "Invalid Solarlife address: %s\n",
|
||||
optarg);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
dst_addr_given = true;
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
dev_id = hci_devid(optarg);
|
||||
if (dev_id < 0) {
|
||||
perror("Invalid adapter");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
//fprintf(stderr, "Invalid option: %c\n", opt);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!argc) {
|
||||
usage(argv);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
optind = 0;
|
||||
|
||||
if (argc) {
|
||||
usage(argv);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (dev_id == -1)
|
||||
bacpy(&src_addr, BDADDR_ANY);
|
||||
else if (hci_devba(dev_id, &src_addr) < 0) {
|
||||
perror("Adapter not available");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!dst_addr_given) {
|
||||
fprintf(stderr, "Destination BT mac address required! Use -h option to print usage.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
/* create the mainloop resources */
|
||||
|
||||
|
||||
mainloop_init();
|
||||
|
||||
fd = l2cap_le_att_connect(&src_addr, &dst_addr, dst_type, sec);
|
||||
if (fd < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
|
||||
cli = client_create(fd, mtu);
|
||||
if (!cli) {
|
||||
close(fd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if(strlen(mqtt_host)) {
|
||||
client_init(&c, mqtt_host, mqtt_port, mqtt_buffer, BUFFER_SIZE_BYTES);
|
||||
assert(client_set_callback(&c, CB_RECEIVED_DATA, got_data) == 1);
|
||||
assert(client_set_callback(&c, CB_ON_CONNECTION, got_connection) == 1);
|
||||
assert(client_set_callback(&c, CB_ON_DISCONNECT, lost_connection) == 1);
|
||||
signal(SIGINT, inthandler);
|
||||
client_connect(&c);
|
||||
} else {
|
||||
printf("MQTT Broker address not given, will not send data.\n");
|
||||
}
|
||||
alarm(1);
|
||||
|
||||
|
||||
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGINT);
|
||||
sigaddset(&mask, SIGTERM);
|
||||
sigaddset(&mask, SIGALRM);
|
||||
|
||||
|
||||
/* add handler for process interrupted (SIGINT) or terminated (SIGTERM)*/
|
||||
mainloop_set_signal(&mask, signal_cb, NULL, NULL);
|
||||
|
||||
mainloop_run();
|
||||
|
||||
printf("\n\nShutting down...\n");
|
||||
if(c.sockfd) client_disconnect(&c);
|
||||
client_destroy(cli);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
481
mainloop.c
Normal file
481
mainloop.c
Normal file
@@ -0,0 +1,481 @@
|
||||
/**
|
||||
* @file mainloop.c
|
||||
* @brief set of function to manage the epool loop
|
||||
* @author Gilbert Brault
|
||||
* @copyright Gilbert Brault 2015
|
||||
* the original work comes from bluez v5.39
|
||||
* value add: documenting main features
|
||||
*
|
||||
*/
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2011-2014 Intel Corporation
|
||||
* Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
|
||||
*
|
||||
*
|
||||
* This library 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 library 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 this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <sys/epoll.h>
|
||||
|
||||
#include "mainloop.h"
|
||||
|
||||
#define MAX_EPOLL_EVENTS 10
|
||||
|
||||
static int epoll_fd;
|
||||
static int epoll_terminate;
|
||||
static int exit_status;
|
||||
|
||||
/**
|
||||
* @brief mainloop file descriptor event data structure
|
||||
*/
|
||||
struct mainloop_data {
|
||||
/// socket or file descriptor, incl. standard i/o
|
||||
int fd;
|
||||
/// epoll event @see EPOLL_EVENTS_DOC
|
||||
uint32_t events;
|
||||
/// call back function(int fd, uint32_t events, void *user_data);
|
||||
mainloop_event_func callback;
|
||||
/// data management call back function(void *user_data);
|
||||
mainloop_destroy_func destroy;
|
||||
/// pointer to a user specific data structure
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
#define MAX_MAINLOOP_ENTRIES 128
|
||||
|
||||
/**
|
||||
* @brief array of file descriptor event stub
|
||||
*/
|
||||
static struct mainloop_data *mainloop_list[MAX_MAINLOOP_ENTRIES];
|
||||
|
||||
struct timeout_data {
|
||||
int fd;
|
||||
mainloop_timeout_func callback;
|
||||
mainloop_destroy_func destroy;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
struct signal_data {
|
||||
int fd;
|
||||
sigset_t mask;
|
||||
mainloop_signal_func callback;
|
||||
mainloop_destroy_func destroy;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
static struct signal_data *signal_data;
|
||||
|
||||
/**
|
||||
* create the epoll resource (epoll_fd global variable)
|
||||
* initialize mainloop_list (global variable) event table
|
||||
* set epoll_terminate to 0 (mainloop_run looping)
|
||||
*/
|
||||
void mainloop_init(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
|
||||
|
||||
for (i = 0; i < MAX_MAINLOOP_ENTRIES; i++)
|
||||
mainloop_list[i] = NULL;
|
||||
|
||||
epoll_terminate = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* set epoll_terminate to 1 (mainloop_run exit looping)
|
||||
*/
|
||||
void mainloop_quit(void)
|
||||
{
|
||||
epoll_terminate = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* set exit_status to EXIT_SUCCESS
|
||||
* set epoll_terminate to 1 (mainloop_run exit looping)
|
||||
*/
|
||||
void mainloop_exit_success(void)
|
||||
{
|
||||
exit_status = EXIT_SUCCESS;
|
||||
epoll_terminate = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* set exit_status to EXIT_FAILURE
|
||||
* set epoll_terminate to 1 (mainloop_run exit looping)
|
||||
*/
|
||||
void mainloop_exit_failure(void)
|
||||
{
|
||||
exit_status = EXIT_FAILURE;
|
||||
epoll_terminate = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fd
|
||||
* @param events
|
||||
* @param user_data
|
||||
*/
|
||||
static void signal_callback(int fd, uint32_t events, void *user_data)
|
||||
{
|
||||
struct signal_data *data = user_data;
|
||||
struct signalfd_siginfo si;
|
||||
ssize_t result;
|
||||
|
||||
if (events & (EPOLLERR | EPOLLHUP)) {
|
||||
mainloop_quit();
|
||||
return;
|
||||
}
|
||||
|
||||
result = read(fd, &si, sizeof(si));
|
||||
if (result != sizeof(si))
|
||||
return;
|
||||
|
||||
if (data->callback)
|
||||
data->callback(si.ssi_signo, data->user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* main loop wait for epoll events
|
||||
* to exit the loop, set epoll_terminate to a <>0 value
|
||||
* @see mainloop_exit_failure
|
||||
* @see mainloop_exit_success
|
||||
*
|
||||
* @return exit_status EXIT_SUCCESS or EXIT_FAILURE
|
||||
*/
|
||||
int mainloop_run(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (signal_data) {
|
||||
if (sigprocmask(SIG_BLOCK, &signal_data->mask, NULL) < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
signal_data->fd = signalfd(-1, &signal_data->mask,
|
||||
SFD_NONBLOCK | SFD_CLOEXEC);
|
||||
if (signal_data->fd < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
if (mainloop_add_fd(signal_data->fd, EPOLLIN,
|
||||
signal_callback, signal_data, NULL) < 0) {
|
||||
close(signal_data->fd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
exit_status = EXIT_SUCCESS;
|
||||
|
||||
while (!epoll_terminate) {
|
||||
struct epoll_event events[MAX_EPOLL_EVENTS];
|
||||
int n, nfds;
|
||||
|
||||
nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, -1);
|
||||
if (nfds < 0)
|
||||
continue;
|
||||
|
||||
for (n = 0; n < nfds; n++) {
|
||||
struct mainloop_data *data = events[n].data.ptr;
|
||||
|
||||
data->callback(data->fd, events[n].events,
|
||||
data->user_data);
|
||||
}
|
||||
}
|
||||
|
||||
if (signal_data) {
|
||||
mainloop_remove_fd(signal_data->fd);
|
||||
close(signal_data->fd);
|
||||
|
||||
if (signal_data->destroy)
|
||||
signal_data->destroy(signal_data->user_data);
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_MAINLOOP_ENTRIES; i++) {
|
||||
struct mainloop_data *data = mainloop_list[i];
|
||||
|
||||
mainloop_list[i] = NULL;
|
||||
|
||||
if (data) {
|
||||
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, data->fd, NULL);
|
||||
|
||||
if (data->destroy)
|
||||
data->destroy(data->user_data);
|
||||
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
|
||||
close(epoll_fd);
|
||||
epoll_fd = 0;
|
||||
|
||||
return exit_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* trigger an event to be processed by the mainloop_run function
|
||||
* and create a mainloop_list entry @see EPOLL_EVENTS_DOC for events description
|
||||
*
|
||||
* @param fd "file descriptor" source of the event (socket)
|
||||
* @param events event flags EPOOL type like EPOLLIN, EPOLLOUT...
|
||||
* @param callback function to call back by the event processor
|
||||
* @param user_data associated data
|
||||
* @param destroy management function to unallocate user_data
|
||||
* @return 0 success else <0 error
|
||||
*/
|
||||
int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback,
|
||||
void *user_data, mainloop_destroy_func destroy)
|
||||
{
|
||||
struct mainloop_data *data;
|
||||
struct epoll_event ev;
|
||||
int err;
|
||||
|
||||
if (fd < 0 || fd > MAX_MAINLOOP_ENTRIES - 1 || !callback)
|
||||
return -EINVAL;
|
||||
|
||||
data = malloc(sizeof(*data));
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(data, 0, sizeof(*data));
|
||||
data->fd = fd;
|
||||
data->events = events;
|
||||
data->callback = callback;
|
||||
data->destroy = destroy;
|
||||
data->user_data = user_data;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = events;
|
||||
ev.data.ptr = data;
|
||||
|
||||
err = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, data->fd, &ev);
|
||||
if (err < 0) {
|
||||
free(data);
|
||||
return err;
|
||||
}
|
||||
|
||||
mainloop_list[fd] = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* trigger an epoll event for an existing mainloop socket (exisiting mainloop_list[fd])
|
||||
* epool event "events" = events, "data.ptr" = mainloop_list[fd]
|
||||
*
|
||||
* @param fd socket
|
||||
* @param events EPOLL event like EPOLLIN, EPOLLOUT...
|
||||
* @return 0==Success <0 error
|
||||
*/
|
||||
int mainloop_modify_fd(int fd, uint32_t events)
|
||||
{
|
||||
struct mainloop_data *data;
|
||||
struct epoll_event ev;
|
||||
int err;
|
||||
|
||||
if (fd < 0 || fd > MAX_MAINLOOP_ENTRIES - 1)
|
||||
return -EINVAL;
|
||||
|
||||
data = mainloop_list[fd];
|
||||
if (!data)
|
||||
return -ENXIO;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = events;
|
||||
ev.data.ptr = data;
|
||||
|
||||
err = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, data->fd, &ev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
data->events = events;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mainloop_remove_fd(int fd)
|
||||
{
|
||||
struct mainloop_data *data;
|
||||
int err;
|
||||
|
||||
if (fd < 0 || fd > MAX_MAINLOOP_ENTRIES - 1)
|
||||
return -EINVAL;
|
||||
|
||||
data = mainloop_list[fd];
|
||||
if (!data)
|
||||
return -ENXIO;
|
||||
|
||||
mainloop_list[fd] = NULL;
|
||||
|
||||
err = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, data->fd, NULL);
|
||||
|
||||
if (data->destroy)
|
||||
data->destroy(data->user_data);
|
||||
|
||||
free(data);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void timeout_destroy(void *user_data)
|
||||
{
|
||||
struct timeout_data *data = user_data;
|
||||
|
||||
close(data->fd);
|
||||
data->fd = -1;
|
||||
|
||||
if (data->destroy)
|
||||
data->destroy(data->user_data);
|
||||
}
|
||||
|
||||
static void timeout_callback(int fd, uint32_t events, void *user_data)
|
||||
{
|
||||
struct timeout_data *data = user_data;
|
||||
uint64_t expired;
|
||||
ssize_t result;
|
||||
|
||||
if (events & (EPOLLERR | EPOLLHUP))
|
||||
return;
|
||||
|
||||
result = read(data->fd, &expired, sizeof(expired));
|
||||
if (result != sizeof(expired))
|
||||
return;
|
||||
|
||||
if (data->callback)
|
||||
data->callback(data->fd, data->user_data);
|
||||
}
|
||||
|
||||
static inline int timeout_set(int fd, unsigned int msec)
|
||||
{
|
||||
struct itimerspec itimer;
|
||||
unsigned int sec = msec / 1000;
|
||||
|
||||
memset(&itimer, 0, sizeof(itimer));
|
||||
itimer.it_interval.tv_sec = 0;
|
||||
itimer.it_interval.tv_nsec = 0;
|
||||
itimer.it_value.tv_sec = sec;
|
||||
itimer.it_value.tv_nsec = (msec - (sec * 1000)) * 1000;
|
||||
|
||||
return timerfd_settime(fd, 0, &itimer, NULL);
|
||||
}
|
||||
|
||||
int mainloop_add_timeout(unsigned int msec, mainloop_timeout_func callback,
|
||||
void *user_data, mainloop_destroy_func destroy)
|
||||
{
|
||||
struct timeout_data *data;
|
||||
|
||||
if (!callback)
|
||||
return -EINVAL;
|
||||
|
||||
data = malloc(sizeof(*data));
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(data, 0, sizeof(*data));
|
||||
data->callback = callback;
|
||||
data->destroy = destroy;
|
||||
data->user_data = user_data;
|
||||
|
||||
data->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
|
||||
if (data->fd < 0) {
|
||||
free(data);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (msec > 0) {
|
||||
if (timeout_set(data->fd, msec) < 0) {
|
||||
close(data->fd);
|
||||
free(data);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (mainloop_add_fd(data->fd, EPOLLIN | EPOLLONESHOT,
|
||||
timeout_callback, data, timeout_destroy) < 0) {
|
||||
close(data->fd);
|
||||
free(data);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return data->fd;
|
||||
}
|
||||
|
||||
int mainloop_modify_timeout(int id, unsigned int msec)
|
||||
{
|
||||
if (msec > 0) {
|
||||
if (timeout_set(id, msec) < 0)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (mainloop_modify_fd(id, EPOLLIN | EPOLLONESHOT) < 0)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mainloop_remove_timeout(int id)
|
||||
{
|
||||
return mainloop_remove_fd(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* set mainloop signal handler (signal_data) usally SIGINT and SIGTERM handler
|
||||
* signal_data is a global variable
|
||||
*
|
||||
* @param mask events to filter
|
||||
* @param callback function call triggered by filtered events prototype callback(int signum, void *user_data)
|
||||
* @param user_data user data to pass to callback
|
||||
* @param destroy user data storage management
|
||||
* @return 0 success else <0 error
|
||||
*/
|
||||
int mainloop_set_signal(sigset_t *mask, mainloop_signal_func callback,
|
||||
void *user_data, mainloop_destroy_func destroy)
|
||||
{
|
||||
struct signal_data *data;
|
||||
|
||||
if (!mask || !callback)
|
||||
return -EINVAL;
|
||||
|
||||
data = malloc(sizeof(*data));
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(data, 0, sizeof(*data));
|
||||
data->callback = callback;
|
||||
data->destroy = destroy;
|
||||
data->user_data = user_data;
|
||||
|
||||
data->fd = -1;
|
||||
memcpy(&data->mask, mask, sizeof(sigset_t));
|
||||
|
||||
free(signal_data);
|
||||
signal_data = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
51
mainloop.h
Normal file
51
mainloop.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2011-2014 Intel Corporation
|
||||
* Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
|
||||
*
|
||||
*
|
||||
* This library 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 library 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 this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/epoll.h>
|
||||
|
||||
typedef void (*mainloop_destroy_func) (void *user_data);
|
||||
|
||||
typedef void (*mainloop_event_func) (int fd, uint32_t events, void *user_data);
|
||||
typedef void (*mainloop_timeout_func) (int id, void *user_data);
|
||||
typedef void (*mainloop_signal_func) (int signum, void *user_data);
|
||||
|
||||
void mainloop_init(void);
|
||||
void mainloop_quit(void);
|
||||
void mainloop_exit_success(void);
|
||||
void mainloop_exit_failure(void);
|
||||
int mainloop_run(void);
|
||||
|
||||
int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback,
|
||||
void *user_data, mainloop_destroy_func destroy);
|
||||
int mainloop_modify_fd(int fd, uint32_t events);
|
||||
int mainloop_remove_fd(int fd);
|
||||
|
||||
int mainloop_add_timeout(unsigned int msec, mainloop_timeout_func callback,
|
||||
void *user_data, mainloop_destroy_func destroy);
|
||||
int mainloop_modify_timeout(int fd, unsigned int msec);
|
||||
int mainloop_remove_timeout(int id);
|
||||
|
||||
int mainloop_set_signal(sigset_t *mask, mainloop_signal_func callback,
|
||||
void *user_data, mainloop_destroy_func destroy);
|
||||
21
makefile
Normal file
21
makefile
Normal file
@@ -0,0 +1,21 @@
|
||||
CC=gcc
|
||||
CFLAGS=-I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -Wall
|
||||
LIBS=-lglib-2.0
|
||||
CC_FRITZ=/usr/src/freetz-ng/toolchain/target/bin/i686-linux-uclibc-gcc-5.5.0
|
||||
CFLAGS_FRITZ=-I/usr/src/freetz-ng/toolchain/target/i686-linux-uclibc/include/glib-2.0 -I/usr/src/freetz-ng/toolchain/target/i686-linux-uclibc/lib/glib-2.0/include -Wall --static -s
|
||||
LIBS_FRITZ=-L/usr/src/freetz-ng/toolchain/target/i686-linux-uclibc/lib -lglib-2.0
|
||||
APPNAME=solarlife
|
||||
|
||||
|
||||
all:
|
||||
$(CC) $(CFLAGS) *.c -o $(APPNAME) $(LIBS)
|
||||
|
||||
|
||||
fritz:
|
||||
$(CC_FRITZ) $(CFLAGS_FRITZ) *.c -o $(APPNAME) $(LIBS_FRITZ)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
rm -f $(APPNAME)
|
||||
|
||||
441
mqtt.c
Normal file
441
mqtt.c
Normal file
@@ -0,0 +1,441 @@
|
||||
#include "mqtt.h"
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
|
||||
static int mqtt_encode_length(unsigned int nbytes, uint8_t* au8data)
|
||||
{
|
||||
int nbytes_encoded = 0;
|
||||
if (nbytes < 0x10000000) /* max MQTT packet size */
|
||||
{
|
||||
unsigned int x = nbytes;
|
||||
do
|
||||
{
|
||||
uint8_t u8digit = x % 128;
|
||||
x /= 128; /* >> 7 */
|
||||
if (x != 0)
|
||||
u8digit |= 0x80;
|
||||
au8data[nbytes_encoded++] = u8digit;
|
||||
}
|
||||
while (x > 0);
|
||||
}
|
||||
return nbytes_encoded;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int mqtt_decode_length(uint8_t* au8data)
|
||||
{
|
||||
int nbytes = 0;
|
||||
int multiplier = 1;
|
||||
unsigned int array_idx = 0;
|
||||
uint8_t u8digit;
|
||||
do
|
||||
{
|
||||
u8digit = au8data[array_idx++];
|
||||
nbytes += (u8digit & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
}
|
||||
while ( (++array_idx < 4)
|
||||
&& ((u8digit & 128) != 0));
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
|
||||
static int mqtt_encode_msg(uint8_t* pu8dst, uint8_t u8ctrl_type, uint8_t u8flgs, uint8_t** apu8data_in, uint32_t* au32input_len, uint32_t u32nargs, uint32_t u32input_len)
|
||||
{
|
||||
int nbytes_encoded = 0;
|
||||
if ( (pu8dst != 0)
|
||||
&& (u8ctrl_type > 0) /* cmd type = [1:14] .. 0 + 15 are reserved */
|
||||
&& (u8ctrl_type < 15)
|
||||
&& (u8flgs < 16) /* flag are 4 bits wide, [0:15] */
|
||||
&& ( (u32nargs == 0) /* Allow for msg's without payload */
|
||||
|| ( (apu8data_in != 0) /* ... otherwise, check the args */
|
||||
&& (au32input_len != 0))))
|
||||
{
|
||||
pu8dst[0] = (u8ctrl_type << 4) | (u8flgs);
|
||||
|
||||
int idx = mqtt_encode_length(u32input_len, &pu8dst[1]);
|
||||
uint32_t i, j;
|
||||
for (i = 0; i < u32nargs; ++i)
|
||||
{
|
||||
for (j = 0; j < au32input_len[i]; ++j, ++nbytes_encoded)
|
||||
{
|
||||
pu8dst[idx + nbytes_encoded + 1] = apu8data_in[i][j];
|
||||
}
|
||||
}
|
||||
nbytes_encoded += (1 + idx);
|
||||
}
|
||||
return nbytes_encoded;
|
||||
}
|
||||
|
||||
|
||||
int mqtt_decode_msg(uint8_t* pu8src, uint8_t* pu8ctrl_type, uint8_t* pu8flgs, uint8_t* pu8data_out, uint32_t* pu32output_len)
|
||||
{
|
||||
int nbytes_decoded = 0;
|
||||
if ( (pu8src != 0)
|
||||
&& (pu8ctrl_type != 0)
|
||||
&& (pu8flgs != 0)
|
||||
&& (pu8data_out != 0)
|
||||
&& (pu32output_len != 0))
|
||||
{
|
||||
*pu8ctrl_type = ((*pu8src) >> 4);
|
||||
*pu8flgs = ((*pu8src) & 0x0F);
|
||||
int idx = 1;
|
||||
int nbytes = mqtt_decode_length(&pu8src[idx]);
|
||||
//printf("nbytes = %d\n", nbytes);
|
||||
if (nbytes < 0x80) /* 0 <= nbytes < 128 */
|
||||
{
|
||||
idx += 1;
|
||||
}
|
||||
else if (nbytes < 0x4000) /* 128 <= nbytes < 16384 */
|
||||
{
|
||||
idx += 2;
|
||||
}
|
||||
else if (nbytes < 0x200000) /* 16384 <= nbytes < 2097152 */
|
||||
{
|
||||
idx += 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
idx += 4;
|
||||
assert(nbytes < 0x10000000);
|
||||
}
|
||||
/* Copy decoded data to output-buffer */
|
||||
int i;
|
||||
for (i = 0; i < nbytes; ++i)
|
||||
{
|
||||
pu8data_out[i] = pu8src[idx + i];
|
||||
}
|
||||
//memcpy(pu8data_out, &pu8src[idx], nbytes);
|
||||
*pu32output_len = nbytes;
|
||||
nbytes_decoded = nbytes + idx;
|
||||
}
|
||||
return nbytes_decoded;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Advanced connect: More options available */
|
||||
int mqtt_encode_connect_msg2(uint8_t* pu8dst, uint8_t u8conn_flgs, uint16_t u16keepalive, uint8_t* pu8clientid, uint16_t u16clientid_len)
|
||||
{
|
||||
int nbytes_encoded = 0;
|
||||
if ( (pu8dst != 0)
|
||||
&& (pu8clientid != 0))
|
||||
{
|
||||
const uint32_t u32hdr_len = 12;
|
||||
uint8_t u8keepalive_msb = (u16keepalive & 0xFF00) >> 8; /* Bug if on Big-Endian machine */
|
||||
uint8_t u8keepalive_lsb = (u16keepalive & 0x00FF);
|
||||
uint8_t u8clientid_len_msb = (u16clientid_len & 0xFF00) >> 8; /* Bug if on Big-Endian machine */
|
||||
uint8_t u8clientid_len_lsb = (u16clientid_len & 0x00FF);
|
||||
uint8_t au8conn_buf[u32hdr_len];
|
||||
/* Variable part of header for CONNECT msg */
|
||||
int idx = 0;
|
||||
au8conn_buf[idx++] = 0x00; /* protocol version length U16 */
|
||||
au8conn_buf[idx++] = 0x04; /* 4 bytes long (MQTT) */
|
||||
au8conn_buf[idx++] = 'M';
|
||||
au8conn_buf[idx++] = 'Q';
|
||||
au8conn_buf[idx++] = 'T';
|
||||
au8conn_buf[idx++] = 'T';
|
||||
au8conn_buf[idx++] = 0x04; /* 4 == MQTT version 3.1.1 */
|
||||
au8conn_buf[idx++] = u8conn_flgs;
|
||||
au8conn_buf[idx++] = u8keepalive_msb;
|
||||
au8conn_buf[idx++] = u8keepalive_lsb;
|
||||
au8conn_buf[idx++] = u8clientid_len_msb;
|
||||
au8conn_buf[idx++] = u8clientid_len_lsb;
|
||||
|
||||
uint8_t* buffers[] = { au8conn_buf, pu8clientid };
|
||||
uint32_t sizes[] = { u32hdr_len, u16clientid_len };
|
||||
|
||||
nbytes_encoded = mqtt_encode_msg(pu8dst, CTRL_CONNECT, 0, buffers, sizes, 2, u32hdr_len + u16clientid_len);
|
||||
}
|
||||
return nbytes_encoded;
|
||||
}
|
||||
|
||||
|
||||
/* Simple connect: No username/password, no QoS etc. */
|
||||
int mqtt_encode_connect_msg(uint8_t* pu8dst, uint8_t* pu8clientid, uint16_t u16clientid_len) /* u8conn_flgs = 2, u16keepalive = 60 */
|
||||
{
|
||||
return mqtt_encode_connect_msg2(pu8dst, 0x02, 60, pu8clientid, u16clientid_len);
|
||||
}
|
||||
|
||||
int mqtt_encode_disconnect_msg(uint8_t* pu8dst)
|
||||
{
|
||||
return mqtt_encode_msg(pu8dst, CTRL_DISCONNECT, 0x02, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
int mqtt_encode_ping_msg(uint8_t* pu8dst)
|
||||
{
|
||||
return mqtt_encode_msg(pu8dst, CTRL_PINGREQ, 0x02, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
int mqtt_encode_publish_msg(uint8_t* pu8dst, uint8_t* pu8topic, uint16_t u16topic_len, uint8_t u8qos, uint16_t u16msg_id, uint8_t* pu8payload, uint32_t u32data_len)
|
||||
{
|
||||
int nbytes_encoded = 0;
|
||||
if (pu8topic != 0)
|
||||
{
|
||||
uint32_t u32msg_len = sizeof(uint16_t) + u16topic_len + sizeof(uint16_t) + u32data_len;
|
||||
uint8_t u8topic_len_msb = (u16topic_len & 0xFF00) >> 8; /* Bug if on Big-Endian machine */
|
||||
uint8_t u8topic_len_lsb = (u16topic_len & 0x00FF);
|
||||
uint8_t u8msg_id_msb = (u16msg_id & 0xFF00) >> 8; /* Bug if on Big-Endian machine */
|
||||
uint8_t u8msg_id_lsb = (u16msg_id & 0x00FF);
|
||||
uint8_t au8topic_len_buf[sizeof(uint16_t)] = { u8topic_len_msb, u8topic_len_lsb };
|
||||
uint8_t au8msg_id_buf[sizeof(uint16_t)] = { u8msg_id_msb, u8msg_id_lsb };
|
||||
uint8_t* buffers[] = { au8topic_len_buf, pu8topic, au8msg_id_buf, pu8payload };
|
||||
uint32_t sizes[] = { sizeof(uint16_t), u16topic_len, sizeof(uint16_t), u32data_len };
|
||||
nbytes_encoded = mqtt_encode_msg(pu8dst, CTRL_PUBLISH, u8qos << 1, buffers, sizes, 4, u32msg_len);
|
||||
}
|
||||
return nbytes_encoded;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static int encode_pubsub_msg2(uint8_t* pu8dst, uint8_t u8ctrl, uint8_t** apu8topic, uint16_t* au16topic_len, uint8_t* au8qos, uint32_t u32nargs, uint16_t u16msg_id)
|
||||
{
|
||||
int nbytes_encoded = 0;
|
||||
if ( (apu8topic != 0)
|
||||
&& (u32nargs < MSG_SUB_MAXNTOPICS))
|
||||
{
|
||||
uint8_t topicsizes[sizeof(uint16_t)][MSG_SUB_MAXNTOPICS];
|
||||
uint32_t sizes[1 + (3 * MSG_SUB_MAXNTOPICS)]; /* for each topic a topic-len, the topic itself, and the qos -- hence 3 * max(subs_pr_msg) */
|
||||
uint8_t* buffers[1 + (3 * MSG_SUB_MAXNTOPICS)]; /* msgid, [topic-len + topic + qos]+ */
|
||||
uint8_t u8msg_id_msb = (u16msg_id & 0xFF00) >> 8; /* Bug if on Big-Endian machine */
|
||||
uint8_t u8msg_id_lsb = (u16msg_id & 0x00FF);
|
||||
uint8_t au8msg_id_buf[sizeof(uint16_t)] = { u8msg_id_msb, u8msg_id_lsb };
|
||||
int bufidx = 0;
|
||||
buffers[bufidx] = au8msg_id_buf;
|
||||
sizes[bufidx++] = sizeof(uint16_t);
|
||||
uint32_t u32msg_len = sizeof(uint16_t); /* msgid */
|
||||
uint8_t u8highest_qos = 0;
|
||||
uint32_t i;
|
||||
for (i = 0; i < u32nargs; ++i)
|
||||
{
|
||||
u32msg_len += sizeof(uint16_t) + au16topic_len[i] + sizeof(uint8_t); /* topic-len + topic + qos */
|
||||
uint8_t u8topic_len_msb = (au16topic_len[i] & 0xFF00) >> 8; /* Bug if on Big-Endian machine */
|
||||
uint8_t u8topic_len_lsb = (au16topic_len[i] & 0x00FF);
|
||||
topicsizes[i][0] = u8topic_len_msb;
|
||||
topicsizes[i][1] = u8topic_len_lsb;
|
||||
buffers[bufidx] = topicsizes[i];
|
||||
sizes[bufidx++] = sizeof(uint16_t);
|
||||
buffers[bufidx] = apu8topic[i];
|
||||
sizes[bufidx++] = au16topic_len[i];
|
||||
buffers[bufidx] = &au8qos[i];
|
||||
sizes[bufidx++] = sizeof(uint8_t);
|
||||
/* Send MQTT SUBSCRIBE msg with highest QoS of all subscribtions */
|
||||
if (au8qos[i] > u8highest_qos)
|
||||
{
|
||||
u8highest_qos = au8qos[i];
|
||||
}
|
||||
}
|
||||
nbytes_encoded = mqtt_encode_msg(pu8dst, u8ctrl, u8highest_qos << 1, buffers, sizes, bufidx, u32msg_len);
|
||||
}
|
||||
return nbytes_encoded;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int mqtt_encode_subscribe_msg(uint8_t* pu8dst, uint8_t* pu8topic, uint16_t u16topic_len, uint8_t u8qos, uint16_t u16msg_id)
|
||||
{
|
||||
return encode_pubsub_msg2(pu8dst, CTRL_SUBSCRIBE, &pu8topic, &u16topic_len, &u8qos, 1, u16msg_id);
|
||||
}
|
||||
|
||||
int mqtt_encode_unsubscribe_msg(uint8_t* pu8dst, uint8_t* pu8topic, uint16_t u16topic_len, uint8_t u8qos, uint16_t u16msg_id)
|
||||
{
|
||||
return encode_pubsub_msg2(pu8dst, CTRL_UNSUBSCRIBE, &pu8topic, &u16topic_len, &u8qos, 1, u16msg_id);
|
||||
}
|
||||
int mqtt_encode_subscribe_msg2(uint8_t* pu8dst, uint8_t** apu8topic, uint16_t* au16topic_len, uint8_t* au8qos, uint32_t u32nargs, uint16_t u16msg_id)
|
||||
{
|
||||
return encode_pubsub_msg2(pu8dst, CTRL_SUBSCRIBE, apu8topic, au16topic_len, au8qos, u32nargs, u16msg_id);
|
||||
}
|
||||
|
||||
|
||||
int mqtt_encode_unsubscribe_msg2(uint8_t* pu8dst, uint8_t** apu8topic, uint16_t* au16topic_len, uint8_t* au8qos, uint32_t u32nargs, uint16_t u16msg_id)
|
||||
{
|
||||
return encode_pubsub_msg2(pu8dst, CTRL_UNSUBSCRIBE, apu8topic, au16topic_len, au8qos, u32nargs, u16msg_id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int mqtt_decode_connack_msg(uint8_t* pu8src, uint32_t u32nbytes)
|
||||
{
|
||||
return ( (pu8src != 0)
|
||||
&& (u32nbytes >= 4)
|
||||
&& (pu8src[0] == 0x20) /* 0x20 : CTRL_CONNACK << 4 */
|
||||
&& (pu8src[1] == 0x02) /* 0x02 : bytes after fixed header */
|
||||
// && (pu8src[3] == 0x00) /* 0x00 : 3rd byte is reserved */
|
||||
&& (pu8src[3] == 0x00)); /* 0x00 : Connection Accepted */
|
||||
}
|
||||
|
||||
|
||||
int mqtt_decode_pingresp_msg(uint8_t* pu8src, uint32_t u32nbytes)
|
||||
{
|
||||
return ( (pu8src != 0)
|
||||
&& (u32nbytes >= 2)
|
||||
&& (pu8src[0] == 0xd0) /* 0xD0 : CTRL_PINGRESP << 4 */
|
||||
&& (pu8src[1] == 0x00)); /* 0x00 : bytes after fixed header */
|
||||
}
|
||||
|
||||
int mqtt_decode_puback_msg(uint8_t* pu8src, uint32_t u32nbytes, uint16_t* pu16msg_id_out)
|
||||
{
|
||||
int success = 0;
|
||||
if ( (pu8src != 0)
|
||||
&& (u32nbytes >= 4)
|
||||
&& (pu8src[0] == 0x40) /* 0x40 : CTRL_PUBACK << 4 */
|
||||
&& (pu8src[1] == 0x02) /* 0x02 : bytes after fixed header */
|
||||
&& (pu16msg_id_out != 0))
|
||||
{
|
||||
*pu16msg_id_out = (pu8src[2] << 8) | pu8src[3];
|
||||
success = 1;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
int mqtt_decode_suback_msg(uint8_t* pu8src, uint32_t u32nbytes, uint16_t* pu16msg_id_out)
|
||||
{
|
||||
int success = 0;
|
||||
if ( (pu8src != 0)
|
||||
&& (u32nbytes >= 4)
|
||||
&& (pu8src[0] == 0x90) /* 0x90 : CTRL_SUBACK << 4 */
|
||||
&& (pu8src[1] == 0x02) /* 0x02 : bytes after fixed header */
|
||||
&& (pu16msg_id_out != 0))
|
||||
{
|
||||
*pu16msg_id_out = (pu8src[2] << 8) | pu8src[3];
|
||||
success = 1;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
int mqtt_decode_publish_msg(uint8_t* pu8src, uint32_t u32nbytes, uint8_t* pu8qos, uint16_t* pu16msg_id_out, uint16_t* pu16topic_len, uint8_t** ppu8topic, uint8_t** ppu8payload)
|
||||
{
|
||||
int success = 0;
|
||||
if ( (pu8src != 0)
|
||||
&& (u32nbytes >= 6)
|
||||
&& (pu8src[0] >> 4 == CTRL_PUBLISH)
|
||||
&& (pu16msg_id_out != 0)
|
||||
&& (pu16topic_len != 0)
|
||||
&& (ppu8topic != 0)
|
||||
&& (ppu8payload != 0) )
|
||||
{
|
||||
*pu8qos = (pu8src[0] >> 1) & 3;
|
||||
uint16_t u16topic_len = (pu8src[2] << 8) | pu8src[3];
|
||||
*pu16topic_len = u16topic_len;
|
||||
*ppu8topic = &pu8src[4];
|
||||
*pu16msg_id_out = (pu8src[4 + u16topic_len] << 8) | pu8src[5 + u16topic_len];
|
||||
*ppu8payload = &pu8src[6 + u16topic_len];
|
||||
success = 1;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if defined(TEST) && (TEST == 1)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// int mqtt_encode_connect_msg2(uint8_t* pu8dst, uint8_t u8conn_flgs, uint16_t u16keepalive, uint8_t* pu8clientid, uint16_t u16clientid_len)
|
||||
uint8_t buf[128];
|
||||
int nbytes;
|
||||
int i;
|
||||
|
||||
nbytes = mqtt_encode_connect_msg2(buf, 0x02, 60, (uint8_t*)"DIGI", 4);
|
||||
printf("con: ");
|
||||
for (i = 0; i < nbytes; ++i)
|
||||
printf("0x%.02x ", buf[i]);
|
||||
printf("\n");
|
||||
|
||||
nbytes = mqtt_encode_connect_msg(buf, (uint8_t*)"DIGI", 4);
|
||||
printf("con: ");
|
||||
for (i = 0; i < nbytes; ++i)
|
||||
printf("0x%.02x ", buf[i]);
|
||||
printf("\n");
|
||||
|
||||
nbytes = mqtt_encode_disconnect_msg(buf);
|
||||
for (i = 0; i < nbytes; ++i)
|
||||
printf("0x%.02x ", buf[i]);
|
||||
printf("\n");
|
||||
|
||||
nbytes = mqtt_encode_ping_msg(buf);
|
||||
for (i = 0; i < nbytes; ++i)
|
||||
printf("0x%.02x ", buf[i]);
|
||||
printf("\n");
|
||||
|
||||
nbytes = mqtt_encode_publish_msg(buf, (uint8_t*)"a/b", 3, 1, 32767, (uint8_t*)"payload", 8);
|
||||
//nbytes = mqtt_encode_publish_msg(buf, (uint8_t*)"a/b", 3, 1, 10, 0, 0);
|
||||
printf("pub: ");
|
||||
for (i = 0; i < nbytes; ++i)
|
||||
{
|
||||
if (buf[i] >= 'A' && buf[i] <= 'z')
|
||||
printf("%c ", buf[i]);
|
||||
else
|
||||
printf("0x%.02x ", buf[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
{
|
||||
uint16_t msg_id, topic_len;
|
||||
uint8_t qos;
|
||||
uint8_t* topic;
|
||||
uint8_t* payload;
|
||||
int rc = mqtt_decode_publish_msg(buf, nbytes, &qos, &msg_id, &topic_len, &topic, &payload);
|
||||
printf("nbytes = %u\n", nbytes);
|
||||
printf("decode pub msg = %d:\n", rc);
|
||||
printf(" qos = %u\n", qos);
|
||||
printf(" msg id = %u\n", msg_id);
|
||||
printf(" topic-len = %u\n", topic_len);
|
||||
printf(" topic = '%s'\n", topic);
|
||||
printf(" payload = '%s'\n", payload);
|
||||
}
|
||||
|
||||
nbytes = mqtt_encode_subscribe_msg(buf, (uint8_t*)"a/b", 3, 1, 32767);
|
||||
printf("sub: ");
|
||||
for (i = 0; i < nbytes; ++i)
|
||||
printf("0x%.02x ", buf[i]);
|
||||
printf("\n");
|
||||
|
||||
uint8_t* tpcs[] = { (uint8_t*)"a/b" };
|
||||
uint16_t tpclens[] = { 3 };
|
||||
uint8_t tpcqos[] = { 1 };
|
||||
nbytes = mqtt_encode_subscribe_msg2(buf, tpcs, tpclens, tpcqos, 1, 32767);
|
||||
printf("sub: ");
|
||||
for (i = 0; i < nbytes; ++i)
|
||||
printf("0x%.02x ", buf[i]);
|
||||
printf("\n");
|
||||
|
||||
nbytes = mqtt_encode_unsubscribe_msg(buf, (uint8_t*)"a/b", 3, 1, 32767);
|
||||
printf("unsub: ");
|
||||
for (i = 0; i < nbytes; ++i)
|
||||
printf("0x%.02x ", buf[i]);
|
||||
printf("\n");
|
||||
|
||||
nbytes = mqtt_encode_unsubscribe_msg2(buf, tpcs, tpclens, tpcqos, 1, 32767);
|
||||
printf("unsub: ");
|
||||
for (i = 0; i < nbytes; ++i)
|
||||
printf("0x%.02x ", buf[i]);
|
||||
printf("\n");
|
||||
|
||||
|
||||
|
||||
uint8_t au8connack_msg[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
printf("connack(msg) = %d \n", mqtt_decode_connack_msg(au8connack_msg, sizeof(au8connack_msg)));
|
||||
|
||||
uint8_t au8pingresp_msg[] = { 0xd0, 0x00 };
|
||||
printf("pingresp(msg) = %d \n", mqtt_decode_pingresp_msg(au8pingresp_msg, sizeof(au8pingresp_msg)));
|
||||
|
||||
uint16_t u16msg_id;
|
||||
uint8_t au8puback_msg[] = { 0x40, 0x02, 0x7f, 0xff };
|
||||
printf("puback(msg) = %d \n", mqtt_decode_puback_msg(au8puback_msg, sizeof(au8puback_msg), &u16msg_id));
|
||||
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
55
mqtt.h
Normal file
55
mqtt.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#include <stdint.h>
|
||||
|
||||
/* Max number of topics one can subscribe to in a single SUBSCRIBE message */
|
||||
#define MSG_SUB_MAXNTOPICS 8
|
||||
|
||||
/* Control Command Types */
|
||||
enum
|
||||
{
|
||||
CTRL_RESERVED, /* 0 */
|
||||
CTRL_CONNECT, /* 1 */
|
||||
CTRL_CONNACK, /* 2 */
|
||||
CTRL_PUBLISH, /* 3 */
|
||||
CTRL_PUBACK, /* 4 */
|
||||
CTRL_PUBREC, /* 5 */
|
||||
CTRL_PUBREL, /* 6 */
|
||||
CTRL_PUBCOMP, /* 7 */
|
||||
CTRL_SUBSCRIBE, /* 8 */
|
||||
CTRL_SUBACK, /* 9 */
|
||||
CTRL_UNSUBSCRIBE, /* 10 */
|
||||
CTRL_UNSUBACK, /* 11 */
|
||||
CTRL_PINGREQ, /* 12 */
|
||||
CTRL_PINGRESP, /* 13 */
|
||||
CTRL_DISCONNECT, /* 14 */
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
QOS_AT_MOST_ONCE, /* 0 - "Fire and Forget" */
|
||||
QOS_AT_LEAST_ONCE, /* 1 - ARQ until ACK */
|
||||
QOS_EXACTLY_ONCE, /* 2 - Transactional delivery */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Simple connect: No username/password, no QoS etc. */
|
||||
int mqtt_encode_connect_msg(uint8_t* pu8dst, uint8_t* pu8clientid, uint16_t u16clientid_len); /* u8conn_flgs = 2, u16keepalive = 60 */
|
||||
/* Advanced connect: More options available */
|
||||
int mqtt_encode_connect_msg2(uint8_t* pu8dst, uint8_t u8conn_flgs, uint16_t u16keepalive, uint8_t* pu8clientid, uint16_t u16clientid_len);
|
||||
int mqtt_encode_disconnect_msg(uint8_t* pu8dst);
|
||||
int mqtt_encode_ping_msg(uint8_t* pu8dst);
|
||||
int mqtt_encode_publish_msg(uint8_t* pu8dst, uint8_t* pu8topic, uint16_t u16topic_len, uint8_t u8qos, uint16_t u16msg_id, uint8_t* pu8payload, uint32_t u32data_len);
|
||||
int mqtt_encode_subscribe_msg(uint8_t* pu8dst, uint8_t* pu8topic, uint16_t u16topic_len, uint8_t u8qos, uint16_t u16msg_id);
|
||||
int mqtt_encode_subscribe_msg2(uint8_t* pu8dst, uint8_t** apu8topic, uint16_t* au16topic_len, uint8_t* au8qos, uint32_t u32nargs, uint16_t u16msg_id);
|
||||
int mqtt_encode_unsubscribe_msg(uint8_t* pu8dst, uint8_t* pu8topic, uint16_t u16topic_len, uint8_t u8qos, uint16_t u16msg_id);
|
||||
int mqtt_encode_unsubscribe_msg2(uint8_t* pu8dst, uint8_t** apu8topic, uint16_t* au16topic_len, uint8_t* au8qos, uint32_t u32nargs, uint16_t u16msg_id);
|
||||
|
||||
int mqtt_decode_connack_msg(uint8_t* pu8src, uint32_t u32nbytes);
|
||||
int mqtt_decode_pingresp_msg(uint8_t* pu8src, uint32_t u32nbytes);
|
||||
int mqtt_decode_puback_msg(uint8_t* pu8src, uint32_t u32nbytes, uint16_t* pu16msg_id);
|
||||
int mqtt_decode_suback_msg(uint8_t* pu8src, uint32_t u32nbytes, uint16_t* pu16msg_id_out);
|
||||
|
||||
int mqtt_decode_msg(uint8_t* pu8src, uint8_t* pu8ctrl_type, uint8_t* pu8flgs, uint8_t* pu8data_out, uint32_t* pu32output_len);
|
||||
int mqtt_decode_publish_msg(uint8_t* pu8src, uint32_t u32nbytes, uint8_t* pu8qos, uint16_t* pu16msg_id_out, uint16_t* pu16topic_len, uint8_t** ppu8topic, uint8_t** ppu8payload);
|
||||
|
||||
|
||||
575
queue.c
Normal file
575
queue.c
Normal file
@@ -0,0 +1,575 @@
|
||||
/**
|
||||
* @file queue.c
|
||||
* @brief set of functions to manage queues
|
||||
* @author Gilbert Brault
|
||||
* @copyright Gilbert Brault 2015
|
||||
* the original work comes from bluez v5.39
|
||||
* value add: documenting main features
|
||||
*
|
||||
*/
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2012-2014 Intel Corporation. All rights reserved.
|
||||
*
|
||||
*
|
||||
* This library 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 library 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 this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "util.h"
|
||||
#include "queue.h"
|
||||
|
||||
struct queue {
|
||||
int ref_count;
|
||||
struct queue_entry *head;
|
||||
struct queue_entry *tail;
|
||||
unsigned int entries;
|
||||
};
|
||||
|
||||
/**
|
||||
* increment &queue->ref_count
|
||||
*
|
||||
* @param queue pointer to the queue structure
|
||||
* @return
|
||||
*/
|
||||
static struct queue *queue_ref(struct queue *queue)
|
||||
{
|
||||
if (!queue)
|
||||
return NULL;
|
||||
|
||||
__sync_fetch_and_add(&queue->ref_count, 1);
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* decrement &queue->ref_count and free queue structure if ref_count == 0
|
||||
*
|
||||
* @param queue pointer to the queue structure
|
||||
*/
|
||||
static void queue_unref(struct queue *queue)
|
||||
{
|
||||
if (__sync_sub_and_fetch(&queue->ref_count, 1))
|
||||
return;
|
||||
|
||||
free(queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new queue structure
|
||||
*
|
||||
* @return queue reference
|
||||
*/
|
||||
struct queue *queue_new(void)
|
||||
{
|
||||
struct queue *queue;
|
||||
|
||||
queue = new0(struct queue, 1);
|
||||
if (!queue)
|
||||
return NULL;
|
||||
|
||||
queue->head = NULL;
|
||||
queue->tail = NULL;
|
||||
queue->entries = 0;
|
||||
|
||||
return queue_ref(queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* destroy all queue structure elements
|
||||
*
|
||||
* @param queue pointer to the queue structure
|
||||
* @param destroy function making data deallocation
|
||||
*/
|
||||
void queue_destroy(struct queue *queue, queue_destroy_func_t destroy)
|
||||
{
|
||||
if (!queue)
|
||||
return;
|
||||
|
||||
queue_remove_all(queue, NULL, NULL, destroy);
|
||||
|
||||
queue_unref(queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* increment &entry->ref_count
|
||||
*
|
||||
* @param entry
|
||||
* @return entry
|
||||
*/
|
||||
static struct queue_entry *queue_entry_ref(struct queue_entry *entry)
|
||||
{
|
||||
if (!entry)
|
||||
return NULL;
|
||||
|
||||
__sync_fetch_and_add(&entry->ref_count, 1);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* decrement &entry->ref_count and free entry structure if ref_count == 0
|
||||
*
|
||||
* @param entry
|
||||
*/
|
||||
static void queue_entry_unref(struct queue_entry *entry)
|
||||
{
|
||||
if (__sync_sub_and_fetch(&entry->ref_count, 1))
|
||||
return;
|
||||
|
||||
free(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new queue, set queue->data to data, increment queue->ref_count
|
||||
*
|
||||
* @param data
|
||||
* @return new queue pointer
|
||||
*/
|
||||
static struct queue_entry *queue_entry_new(void *data)
|
||||
{
|
||||
struct queue_entry *entry;
|
||||
|
||||
entry = new0(struct queue_entry, 1);
|
||||
if (!entry)
|
||||
return NULL;
|
||||
|
||||
entry->data = data;
|
||||
|
||||
return queue_entry_ref(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* push a queue entry allocated with data and set it at the tail of queue
|
||||
*
|
||||
* @param queue queue pointer where to allocate data
|
||||
* @param data data to queue
|
||||
* @return
|
||||
*/
|
||||
bool queue_push_tail(struct queue *queue, void *data)
|
||||
{
|
||||
struct queue_entry *entry;
|
||||
|
||||
if (!queue)
|
||||
return false;
|
||||
|
||||
entry = queue_entry_new(data);
|
||||
if (!entry)
|
||||
return false;
|
||||
|
||||
if (queue->tail)
|
||||
queue->tail->next = entry;
|
||||
|
||||
queue->tail = entry;
|
||||
|
||||
if (!queue->head)
|
||||
queue->head = entry;
|
||||
|
||||
queue->entries++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* push a queue entry allocated with data and set it at the head of queue
|
||||
*
|
||||
* @param queue queue pointer where to allocate data
|
||||
* @param data data to queue
|
||||
* @return
|
||||
*/
|
||||
bool queue_push_head(struct queue *queue, void *data)
|
||||
{
|
||||
struct queue_entry *entry;
|
||||
|
||||
if (!queue)
|
||||
return false;
|
||||
|
||||
entry = queue_entry_new(data);
|
||||
if (!entry)
|
||||
return false;
|
||||
|
||||
entry->next = queue->head;
|
||||
|
||||
queue->head = entry;
|
||||
|
||||
if (!queue->tail)
|
||||
queue->tail = entry;
|
||||
|
||||
queue->entries++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* push a queue entry allocated with data and set it after the entry
|
||||
*
|
||||
* @param queue queue pointer where to allocate data
|
||||
* @param entry element queue where to put the data after
|
||||
* @param data data to queue
|
||||
* @return
|
||||
*/
|
||||
bool queue_push_after(struct queue *queue, void *entry, void *data)
|
||||
{
|
||||
struct queue_entry *qentry, *tmp, *new_entry;
|
||||
|
||||
qentry = NULL;
|
||||
|
||||
if (!queue)
|
||||
return false;
|
||||
|
||||
for (tmp = queue->head; tmp; tmp = tmp->next) {
|
||||
if (tmp->data == entry) {
|
||||
qentry = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!qentry)
|
||||
return false;
|
||||
|
||||
new_entry = queue_entry_new(data);
|
||||
if (!new_entry)
|
||||
return false;
|
||||
|
||||
new_entry->next = qentry->next;
|
||||
|
||||
if (!qentry->next)
|
||||
queue->tail = new_entry;
|
||||
|
||||
qentry->next = new_entry;
|
||||
queue->entries++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* pop data from the head of queue
|
||||
*
|
||||
* @param queue queue pointer
|
||||
*/
|
||||
void *queue_pop_head(struct queue *queue)
|
||||
{
|
||||
struct queue_entry *entry;
|
||||
void *data;
|
||||
|
||||
if (!queue || !queue->head)
|
||||
return NULL;
|
||||
|
||||
entry = queue->head;
|
||||
|
||||
if (!queue->head->next) {
|
||||
queue->head = NULL;
|
||||
queue->tail = NULL;
|
||||
} else
|
||||
queue->head = queue->head->next;
|
||||
|
||||
data = entry->data;
|
||||
|
||||
queue_entry_unref(entry);
|
||||
queue->entries--;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* peek data from the head of the queue
|
||||
*
|
||||
* @param queue queue pointer
|
||||
*/
|
||||
void *queue_peek_head(struct queue *queue)
|
||||
{
|
||||
if (!queue || !queue->head)
|
||||
return NULL;
|
||||
|
||||
return queue->head->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* peek data from the tail of the queue
|
||||
*
|
||||
* @param queue queue pointer
|
||||
*/
|
||||
void *queue_peek_tail(struct queue *queue)
|
||||
{
|
||||
if (!queue || !queue->tail)
|
||||
return NULL;
|
||||
|
||||
return queue->tail->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* iterator for the queue
|
||||
*
|
||||
* @param queue queue pointer
|
||||
* @param function function(void *data, void *user_data) to call for each element
|
||||
* @param user_data user pointer to pass to function
|
||||
*/
|
||||
void queue_foreach(struct queue *queue, queue_foreach_func_t function,
|
||||
void *user_data)
|
||||
{
|
||||
struct queue_entry *entry;
|
||||
|
||||
if (!queue || !function)
|
||||
return;
|
||||
|
||||
entry = queue->head;
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
queue_ref(queue);
|
||||
while (entry && queue->head && queue->ref_count > 1) {
|
||||
struct queue_entry *next;
|
||||
|
||||
queue_entry_ref(entry);
|
||||
|
||||
function(entry->data, user_data);
|
||||
|
||||
next = entry->next;
|
||||
|
||||
queue_entry_unref(entry);
|
||||
|
||||
entry = next;
|
||||
}
|
||||
queue_unref(queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* compare a with b
|
||||
*
|
||||
* @param a first parameter
|
||||
* @param b second parameter
|
||||
*
|
||||
* @return true if both element match
|
||||
*/
|
||||
static bool direct_match(const void *a, const void *b)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param queue queue pointer
|
||||
* @param function function call to make comparison
|
||||
* @param match_data data to match with queue entry
|
||||
*
|
||||
* @return found entry and return pointer to user data (queued data) or NULL if not found
|
||||
*/
|
||||
void *queue_find(struct queue *queue, queue_match_func_t function,
|
||||
const void *match_data)
|
||||
{
|
||||
struct queue_entry *entry;
|
||||
|
||||
if (!queue)
|
||||
return NULL;
|
||||
|
||||
if (!function)
|
||||
function = direct_match;
|
||||
|
||||
for (entry = queue->head; entry; entry = entry->next)
|
||||
if (function(entry->data, match_data))
|
||||
return entry->data;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove queue element holding data
|
||||
*
|
||||
* @param queue queue pointer
|
||||
* @param data data pointer
|
||||
*
|
||||
* @return false if not found, true if element removed
|
||||
*/
|
||||
bool queue_remove(struct queue *queue, void *data)
|
||||
{
|
||||
struct queue_entry *entry, *prev;
|
||||
|
||||
if (!queue)
|
||||
return false;
|
||||
|
||||
for (entry = queue->head, prev = NULL; entry;
|
||||
prev = entry, entry = entry->next) {
|
||||
if (entry->data != data)
|
||||
continue;
|
||||
|
||||
if (prev)
|
||||
prev->next = entry->next;
|
||||
else
|
||||
queue->head = entry->next;
|
||||
|
||||
if (!entry->next)
|
||||
queue->tail = prev;
|
||||
|
||||
queue_entry_unref(entry);
|
||||
queue->entries--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove element match by function(void *data, void *match_data) where match_data = user_data
|
||||
*
|
||||
* @param queue queue pointer
|
||||
* @param function function call to make comparison
|
||||
* @param user_data data to match queue element in function
|
||||
*
|
||||
* @return NULL if not found or queue entry matching criteria
|
||||
*/
|
||||
void *queue_remove_if(struct queue *queue, queue_match_func_t function,
|
||||
void *user_data)
|
||||
{
|
||||
struct queue_entry *entry, *prev = NULL;
|
||||
|
||||
if (!queue || !function)
|
||||
return NULL;
|
||||
|
||||
entry = queue->head;
|
||||
|
||||
while (entry) {
|
||||
if (function(entry->data, user_data)) {
|
||||
void *data;
|
||||
|
||||
if (prev)
|
||||
prev->next = entry->next;
|
||||
else
|
||||
queue->head = entry->next;
|
||||
|
||||
if (!entry->next)
|
||||
queue->tail = prev;
|
||||
|
||||
data = entry->data;
|
||||
|
||||
queue_entry_unref(entry);
|
||||
queue->entries--;
|
||||
|
||||
return data;
|
||||
} else {
|
||||
prev = entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove all queue element
|
||||
*
|
||||
* @param queue queue pointer
|
||||
* @param function function used to match data deallocation
|
||||
* @param user_data data pointer used in deallocation function
|
||||
* @param destroy function for data deallocation
|
||||
*
|
||||
* @return count of element destroyed (0 if none)
|
||||
*/
|
||||
unsigned int queue_remove_all(struct queue *queue, queue_match_func_t function,
|
||||
void *user_data, queue_destroy_func_t destroy)
|
||||
{
|
||||
struct queue_entry *entry;
|
||||
unsigned int count = 0;
|
||||
|
||||
if (!queue)
|
||||
return 0;
|
||||
|
||||
entry = queue->head;
|
||||
|
||||
if (function) {
|
||||
while (entry) {
|
||||
void *data;
|
||||
unsigned int entries = queue->entries;
|
||||
|
||||
data = queue_remove_if(queue, function, user_data);
|
||||
if (entries == queue->entries)
|
||||
break;
|
||||
|
||||
if (destroy)
|
||||
destroy(data);
|
||||
|
||||
count++;
|
||||
}
|
||||
} else {
|
||||
queue->head = NULL;
|
||||
queue->tail = NULL;
|
||||
queue->entries = 0;
|
||||
|
||||
while (entry) {
|
||||
struct queue_entry *tmp = entry;
|
||||
|
||||
entry = entry->next;
|
||||
|
||||
if (destroy)
|
||||
destroy(tmp->data);
|
||||
|
||||
queue_entry_unref(tmp);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* return queue head pointer
|
||||
*
|
||||
* @param queue queue pointer
|
||||
*
|
||||
* @return queue head
|
||||
*/
|
||||
const struct queue_entry *queue_get_entries(struct queue *queue)
|
||||
{
|
||||
if (!queue)
|
||||
return NULL;
|
||||
|
||||
return queue->head;
|
||||
}
|
||||
|
||||
/**
|
||||
* return queue length
|
||||
*
|
||||
* @param queue queue pointer
|
||||
*
|
||||
* @return return 0 or queue elements count
|
||||
*/
|
||||
unsigned int queue_length(struct queue *queue)
|
||||
{
|
||||
if (!queue)
|
||||
return 0;
|
||||
|
||||
return queue->entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* test if queue is empty
|
||||
*
|
||||
* @param queue queue pointer
|
||||
* @return
|
||||
*/
|
||||
bool queue_isempty(struct queue *queue)
|
||||
{
|
||||
if (!queue)
|
||||
return true;
|
||||
|
||||
return queue->entries == 0;
|
||||
}
|
||||
65
queue.h
Normal file
65
queue.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2012-2014 Intel Corporation. All rights reserved.
|
||||
*
|
||||
*
|
||||
* This library 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 library 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 this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef void (*queue_destroy_func_t)(void *data);
|
||||
|
||||
struct queue;
|
||||
|
||||
struct queue_entry {
|
||||
int ref_count;
|
||||
void *data;
|
||||
struct queue_entry *next;
|
||||
};
|
||||
|
||||
struct queue *queue_new(void);
|
||||
void queue_destroy(struct queue *queue, queue_destroy_func_t destroy);
|
||||
|
||||
bool queue_push_tail(struct queue *queue, void *data);
|
||||
bool queue_push_head(struct queue *queue, void *data);
|
||||
bool queue_push_after(struct queue *queue, void *entry, void *data);
|
||||
void *queue_pop_head(struct queue *queue);
|
||||
void *queue_peek_head(struct queue *queue);
|
||||
void *queue_peek_tail(struct queue *queue);
|
||||
|
||||
typedef void (*queue_foreach_func_t)(void *data, void *user_data);
|
||||
|
||||
void queue_foreach(struct queue *queue, queue_foreach_func_t function,
|
||||
void *user_data);
|
||||
|
||||
typedef bool (*queue_match_func_t)(const void *data, const void *match_data);
|
||||
|
||||
void *queue_find(struct queue *queue, queue_match_func_t function,
|
||||
const void *match_data);
|
||||
|
||||
bool queue_remove(struct queue *queue, void *data);
|
||||
void *queue_remove_if(struct queue *queue, queue_match_func_t function,
|
||||
void *user_data);
|
||||
unsigned int queue_remove_all(struct queue *queue, queue_match_func_t function,
|
||||
void *user_data, queue_destroy_func_t destroy);
|
||||
|
||||
const struct queue_entry *queue_get_entries(struct queue *queue);
|
||||
|
||||
unsigned int queue_length(struct queue *queue);
|
||||
bool queue_isempty(struct queue *queue);
|
||||
86
timeout-glib.c
Normal file
86
timeout-glib.c
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* @file timeout-glib.c
|
||||
* @author Gilbert Brault
|
||||
* @copyright Gilbert Brault 2015
|
||||
* the original work comes from bluez v5.39
|
||||
* value add: documenting main features
|
||||
*
|
||||
*/
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2014 Intel Corporation. All rights reserved.
|
||||
*
|
||||
*
|
||||
* This library 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 library 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "timeout.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
struct timeout_data {
|
||||
timeout_func_t func;
|
||||
timeout_destroy_func_t destroy;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
static gboolean timeout_callback(gpointer user_data)
|
||||
{
|
||||
struct timeout_data *data = user_data;
|
||||
|
||||
if (data->func(data->user_data))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void timeout_destroy(gpointer user_data)
|
||||
{
|
||||
struct timeout_data *data = user_data;
|
||||
|
||||
if (data->destroy)
|
||||
data->destroy(data->user_data);
|
||||
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
unsigned int timeout_add(unsigned int timeout, timeout_func_t func,
|
||||
void *user_data, timeout_destroy_func_t destroy)
|
||||
{
|
||||
struct timeout_data *data;
|
||||
guint id;
|
||||
|
||||
data = g_try_new0(struct timeout_data, 1);
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
data->func = func;
|
||||
data->destroy = destroy;
|
||||
data->user_data = user_data;
|
||||
|
||||
id = g_timeout_add_full(G_PRIORITY_DEFAULT, timeout, timeout_callback,
|
||||
data, timeout_destroy);
|
||||
if (!id)
|
||||
g_free(data);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void timeout_remove(unsigned int id)
|
||||
{
|
||||
GSource *source = g_main_context_find_source_by_id(NULL, id);
|
||||
|
||||
if (source)
|
||||
g_source_destroy(source);
|
||||
}
|
||||
27
timeout.h
Normal file
27
timeout.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2014 Intel Corporation. All rights reserved.
|
||||
*
|
||||
*
|
||||
* This library 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 library 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef bool (*timeout_func_t)(void *user_data);
|
||||
typedef void (*timeout_destroy_func_t)(void *user_data);
|
||||
|
||||
unsigned int timeout_add(unsigned int timeout, timeout_func_t func,
|
||||
void *user_data, timeout_destroy_func_t destroy);
|
||||
void timeout_remove(unsigned int id);
|
||||
178
util.c
Normal file
178
util.c
Normal file
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* @file util.c
|
||||
* @brief set of utility functions
|
||||
* @author Gilbert Brault
|
||||
* @copyright Gilbert Brault 2015
|
||||
* the original work comes from bluez v5.39
|
||||
* value add: documenting main features
|
||||
*
|
||||
*/
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2012-2014 Intel Corporation. All rights reserved.
|
||||
*
|
||||
*
|
||||
* This library 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 library 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 this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
/**
|
||||
* create a str debug message using format and then call function(str,user_data)
|
||||
*
|
||||
* @param function function to call
|
||||
* @param user_data data for the "function"
|
||||
* @param format format string template of str string
|
||||
*/
|
||||
void util_debug(util_debug_func_t function, void *user_data,
|
||||
const char *format, ...)
|
||||
{
|
||||
char str[78];
|
||||
va_list ap;
|
||||
|
||||
if (!function || !format)
|
||||
return;
|
||||
|
||||
va_start(ap, format);
|
||||
vsnprintf(str, sizeof(str), format, ap);
|
||||
va_end(ap);
|
||||
|
||||
function(str, user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* hexadecimal dump utility: create the str hex string and then call function(str,user_data)
|
||||
*
|
||||
* @param dir first char of str
|
||||
* @param buf buffer to convert to hex (str)
|
||||
* @param len size of buffer (should be less than or equal to 16)
|
||||
* @param function function to call with (str,user_data)
|
||||
* @param user_data pointer to pass to function
|
||||
*/
|
||||
void util_hexdump(const char dir, const unsigned char *buf, size_t len,
|
||||
util_debug_func_t function, void *user_data)
|
||||
{
|
||||
static const char hexdigits[] = "0123456789abcdef";
|
||||
char str[68];
|
||||
size_t i;
|
||||
|
||||
if (!function || !len)
|
||||
return;
|
||||
|
||||
str[0] = dir;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
str[((i % 16) * 3) + 1] = ' ';
|
||||
str[((i % 16) * 3) + 2] = hexdigits[buf[i] >> 4];
|
||||
str[((i % 16) * 3) + 3] = hexdigits[buf[i] & 0xf];
|
||||
str[(i % 16) + 51] = isprint(buf[i]) ? buf[i] : '.';
|
||||
|
||||
if ((i + 1) % 16 == 0) {
|
||||
str[49] = ' ';
|
||||
str[50] = ' ';
|
||||
str[67] = '\0';
|
||||
function(str, user_data);
|
||||
str[0] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
if (i % 16 > 0) {
|
||||
size_t j;
|
||||
for (j = (i % 16); j < 16; j++) {
|
||||
str[(j * 3) + 1] = ' ';
|
||||
str[(j * 3) + 2] = ' ';
|
||||
str[(j * 3) + 3] = ' ';
|
||||
str[j + 51] = ' ';
|
||||
}
|
||||
str[49] = ' ';
|
||||
str[50] = ' ';
|
||||
str[67] = '\0';
|
||||
function(str, user_data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for getting the dirent type in case readdir
|
||||
*
|
||||
* @param parent prefix to build full path
|
||||
* @param name suffix to builld full path
|
||||
* @return DT_UNKNOWN or DT_DIR if parent/name is a DIR
|
||||
*/
|
||||
unsigned char util_get_dt(const char *parent, const char *name)
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
struct stat st;
|
||||
|
||||
snprintf(filename, sizeof(filename), "%s/%s", parent, name);
|
||||
if (lstat(filename, &st) == 0 && S_ISDIR(st.st_mode))
|
||||
return DT_DIR;
|
||||
|
||||
return DT_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helpers for bitfield operations
|
||||
*
|
||||
* Find unique id in range from 1 to max but no bigger then
|
||||
* sizeof(int) * 8. ffs() is used since it is POSIX standard
|
||||
*
|
||||
* @param bitmap map of bits
|
||||
* @param max max bit position
|
||||
* @return
|
||||
*/
|
||||
uint8_t util_get_uid(unsigned int *bitmap, uint8_t max)
|
||||
{
|
||||
uint8_t id;
|
||||
|
||||
id = ffs(~*bitmap);
|
||||
|
||||
if (!id || id > max)
|
||||
return 0;
|
||||
|
||||
*bitmap |= 1 << (id - 1);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear id bit in bitmap
|
||||
*
|
||||
* @param bitmap map of bits
|
||||
* @param id bit rank to clear
|
||||
*/
|
||||
void util_clear_uid(unsigned int *bitmap, uint8_t id)
|
||||
{
|
||||
if (!id)
|
||||
return;
|
||||
|
||||
*bitmap &= ~(1 << (id - 1));
|
||||
}
|
||||
158
util.h
Normal file
158
util.h
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2012-2014 Intel Corporation. All rights reserved.
|
||||
*
|
||||
*
|
||||
* This library 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 library 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 this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <alloca.h>
|
||||
#include <byteswap.h>
|
||||
#include <string.h>
|
||||
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define le16_to_cpu(val) (val)
|
||||
#define le32_to_cpu(val) (val)
|
||||
#define le64_to_cpu(val) (val)
|
||||
#define cpu_to_le16(val) (val)
|
||||
#define cpu_to_le32(val) (val)
|
||||
#define cpu_to_le64(val) (val)
|
||||
#define be16_to_cpu(val) bswap_16(val)
|
||||
#define be32_to_cpu(val) bswap_32(val)
|
||||
#define be64_to_cpu(val) bswap_64(val)
|
||||
#define cpu_to_be16(val) bswap_16(val)
|
||||
#define cpu_to_be32(val) bswap_32(val)
|
||||
#define cpu_to_be64(val) bswap_64(val)
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
#define le16_to_cpu(val) bswap_16(val)
|
||||
#define le32_to_cpu(val) bswap_32(val)
|
||||
#define le64_to_cpu(val) bswap_64(val)
|
||||
#define cpu_to_le16(val) bswap_16(val)
|
||||
#define cpu_to_le32(val) bswap_32(val)
|
||||
#define cpu_to_le64(val) bswap_64(val)
|
||||
#define be16_to_cpu(val) (val)
|
||||
#define be32_to_cpu(val) (val)
|
||||
#define be64_to_cpu(val) (val)
|
||||
#define cpu_to_be16(val) (val)
|
||||
#define cpu_to_be32(val) (val)
|
||||
#define cpu_to_be64(val) (val)
|
||||
#else
|
||||
#error "Unknown byte order"
|
||||
#endif
|
||||
|
||||
#define get_unaligned(ptr) \
|
||||
__extension__ ({ \
|
||||
struct __attribute__((packed)) { \
|
||||
__typeof__(*(ptr)) __v; \
|
||||
} *__p = (__typeof__(__p)) (ptr); \
|
||||
__p->__v; \
|
||||
})
|
||||
|
||||
#define put_unaligned(val, ptr) \
|
||||
do { \
|
||||
struct __attribute__((packed)) { \
|
||||
__typeof__(*(ptr)) __v; \
|
||||
} *__p = (__typeof__(__p)) (ptr); \
|
||||
__p->__v = (val); \
|
||||
} while (0)
|
||||
|
||||
#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
|
||||
#define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u)))
|
||||
|
||||
#define PTR_TO_INT(p) ((int) ((intptr_t) (p)))
|
||||
#define INT_TO_PTR(u) ((void *) ((intptr_t) (u)))
|
||||
|
||||
#define new0(t, n) ((t*) calloc((n), sizeof(t)))
|
||||
#define newa(t, n) ((t*) alloca(sizeof(t)*(n)))
|
||||
#define malloc0(n) (calloc((n), 1))
|
||||
|
||||
typedef void (*util_debug_func_t)(const char *str, void *user_data);
|
||||
|
||||
void util_debug(util_debug_func_t function, void *user_data,
|
||||
const char *format, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
|
||||
void util_hexdump(const char dir, const unsigned char *buf, size_t len,
|
||||
util_debug_func_t function, void *user_data);
|
||||
|
||||
unsigned char util_get_dt(const char *parent, const char *name);
|
||||
|
||||
uint8_t util_get_uid(unsigned int *bitmap, uint8_t max);
|
||||
void util_clear_uid(unsigned int *bitmap, uint8_t id);
|
||||
|
||||
static inline uint16_t get_le16(const void *ptr)
|
||||
{
|
||||
return le16_to_cpu(get_unaligned((const uint16_t *) ptr));
|
||||
}
|
||||
|
||||
static inline uint16_t get_be16(const void *ptr)
|
||||
{
|
||||
return be16_to_cpu(get_unaligned((const uint16_t *) ptr));
|
||||
}
|
||||
|
||||
static inline uint32_t get_le32(const void *ptr)
|
||||
{
|
||||
return le32_to_cpu(get_unaligned((const uint32_t *) ptr));
|
||||
}
|
||||
|
||||
static inline uint32_t get_be32(const void *ptr)
|
||||
{
|
||||
return be32_to_cpu(get_unaligned((const uint32_t *) ptr));
|
||||
}
|
||||
|
||||
static inline uint64_t get_le64(const void *ptr)
|
||||
{
|
||||
return le64_to_cpu(get_unaligned((const uint64_t *) ptr));
|
||||
}
|
||||
|
||||
static inline uint64_t get_be64(const void *ptr)
|
||||
{
|
||||
return be64_to_cpu(get_unaligned((const uint64_t *) ptr));
|
||||
}
|
||||
|
||||
static inline void put_le16(uint16_t val, void *dst)
|
||||
{
|
||||
put_unaligned(cpu_to_le16(val), (uint16_t *) dst);
|
||||
}
|
||||
|
||||
static inline void put_be16(uint16_t val, const void *ptr)
|
||||
{
|
||||
put_unaligned(cpu_to_be16(val), (uint16_t *) ptr);
|
||||
}
|
||||
|
||||
static inline void put_le32(uint32_t val, void *dst)
|
||||
{
|
||||
put_unaligned(cpu_to_le32(val), (uint32_t *) dst);
|
||||
}
|
||||
|
||||
static inline void put_be32(uint32_t val, void *dst)
|
||||
{
|
||||
put_unaligned(cpu_to_be32(val), (uint32_t *) dst);
|
||||
}
|
||||
|
||||
static inline void put_le64(uint64_t val, void *dst)
|
||||
{
|
||||
put_unaligned(cpu_to_le64(val), (uint64_t *) dst);
|
||||
}
|
||||
|
||||
static inline void put_be64(uint64_t val, void *dst)
|
||||
{
|
||||
put_unaligned(cpu_to_be64(val), (uint64_t *) dst);
|
||||
}
|
||||
324
uuid.c
Normal file
324
uuid.c
Normal file
@@ -0,0 +1,324 @@
|
||||
/**
|
||||
* @file uuid.c
|
||||
* @brief uuid collection of functions
|
||||
* @author Gilbert Brault
|
||||
* @copyright Gilbert Brault 2015
|
||||
* the original work comes from bluez v5.39
|
||||
* value add: documenting main features
|
||||
*
|
||||
*/
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2011 Nokia Corporation
|
||||
* Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "bluetooth.h"
|
||||
#include "uuid.h"
|
||||
|
||||
static uint128_t bluetooth_base_uuid = {
|
||||
.data = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
|
||||
0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }
|
||||
};
|
||||
|
||||
#define BASE_UUID16_OFFSET 2
|
||||
#define BASE_UUID32_OFFSET 0
|
||||
|
||||
static void bt_uuid16_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst)
|
||||
{
|
||||
uint16_t be16;
|
||||
|
||||
dst->value.u128 = bluetooth_base_uuid;
|
||||
dst->type = BT_UUID128;
|
||||
|
||||
/*
|
||||
* No matter the system: 128-bit UUIDs should be stored
|
||||
* as big-endian. 16-bit UUIDs are stored on host order.
|
||||
*/
|
||||
|
||||
be16 = htons(src->value.u16);
|
||||
memcpy(&dst->value.u128.data[BASE_UUID16_OFFSET], &be16, sizeof(be16));
|
||||
}
|
||||
|
||||
static void bt_uuid32_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst)
|
||||
{
|
||||
uint32_t be32;
|
||||
|
||||
dst->value.u128 = bluetooth_base_uuid;
|
||||
dst->type = BT_UUID128;
|
||||
|
||||
/*
|
||||
* No matter the system: 128-bit UUIDs should be stored
|
||||
* as big-endian. 32-bit UUIDs are stored on host order.
|
||||
*/
|
||||
|
||||
be32 = htonl(src->value.u32);
|
||||
memcpy(&dst->value.u128.data[BASE_UUID32_OFFSET], &be32, sizeof(be32));
|
||||
}
|
||||
|
||||
void bt_uuid_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst)
|
||||
{
|
||||
switch (src->type) {
|
||||
case BT_UUID128:
|
||||
*dst = *src;
|
||||
break;
|
||||
case BT_UUID32:
|
||||
bt_uuid32_to_uuid128(src, dst);
|
||||
break;
|
||||
case BT_UUID16:
|
||||
bt_uuid16_to_uuid128(src, dst);
|
||||
break;
|
||||
case BT_UUID_UNSPEC:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int bt_uuid128_cmp(const bt_uuid_t *u1, const bt_uuid_t *u2)
|
||||
{
|
||||
return memcmp(&u1->value.u128, &u2->value.u128, sizeof(uint128_t));
|
||||
}
|
||||
|
||||
int bt_uuid16_create(bt_uuid_t *btuuid, uint16_t value)
|
||||
{
|
||||
memset(btuuid, 0, sizeof(bt_uuid_t));
|
||||
btuuid->type = BT_UUID16;
|
||||
btuuid->value.u16 = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_uuid32_create(bt_uuid_t *btuuid, uint32_t value)
|
||||
{
|
||||
memset(btuuid, 0, sizeof(bt_uuid_t));
|
||||
btuuid->type = BT_UUID32;
|
||||
btuuid->value.u32 = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_uuid128_create(bt_uuid_t *btuuid, uint128_t value)
|
||||
{
|
||||
memset(btuuid, 0, sizeof(bt_uuid_t));
|
||||
btuuid->type = BT_UUID128;
|
||||
btuuid->value.u128 = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_uuid_cmp(const bt_uuid_t *uuid1, const bt_uuid_t *uuid2)
|
||||
{
|
||||
bt_uuid_t u1, u2;
|
||||
|
||||
bt_uuid_to_uuid128(uuid1, &u1);
|
||||
bt_uuid_to_uuid128(uuid2, &u2);
|
||||
|
||||
return bt_uuid128_cmp(&u1, &u2);
|
||||
}
|
||||
|
||||
/*
|
||||
* convert the UUID to string, copying a maximum of n characters.
|
||||
*/
|
||||
int bt_uuid_to_string(const bt_uuid_t *uuid, char *str, size_t n)
|
||||
{
|
||||
if (!uuid) {
|
||||
snprintf(str, n, "NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (uuid->type) {
|
||||
case BT_UUID16:
|
||||
snprintf(str, n, "%.4x", uuid->value.u16);
|
||||
break;
|
||||
case BT_UUID32:
|
||||
snprintf(str, n, "%.8x", uuid->value.u32);
|
||||
break;
|
||||
case BT_UUID128: {
|
||||
unsigned int data0;
|
||||
unsigned short data1;
|
||||
unsigned short data2;
|
||||
unsigned short data3;
|
||||
unsigned int data4;
|
||||
unsigned short data5;
|
||||
|
||||
const uint8_t *data = (uint8_t *) &uuid->value.u128;
|
||||
|
||||
memcpy(&data0, &data[0], 4);
|
||||
memcpy(&data1, &data[4], 2);
|
||||
memcpy(&data2, &data[6], 2);
|
||||
memcpy(&data3, &data[8], 2);
|
||||
memcpy(&data4, &data[10], 4);
|
||||
memcpy(&data5, &data[14], 2);
|
||||
|
||||
snprintf(str, n, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
|
||||
ntohl(data0), ntohs(data1),
|
||||
ntohs(data2), ntohs(data3),
|
||||
ntohl(data4), ntohs(data5));
|
||||
}
|
||||
break;
|
||||
case BT_UUID_UNSPEC:
|
||||
default:
|
||||
snprintf(str, n, "Type of UUID (%x) unknown.", uuid->type);
|
||||
return -EINVAL; /* Enum type of UUID not set */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_uuid128(const char *string)
|
||||
{
|
||||
return (strlen(string) == 36 &&
|
||||
string[8] == '-' &&
|
||||
string[13] == '-' &&
|
||||
string[18] == '-' &&
|
||||
string[23] == '-');
|
||||
}
|
||||
|
||||
static inline int is_base_uuid128(const char *string)
|
||||
{
|
||||
uint16_t uuid;
|
||||
char dummy;
|
||||
|
||||
if (!is_uuid128(string))
|
||||
return 0;
|
||||
|
||||
return sscanf(string,
|
||||
"0000%04hx-0000-1000-8000-00805%1[fF]9%1[bB]34%1[fF]%1[bB]",
|
||||
&uuid, &dummy, &dummy, &dummy, &dummy) == 5;
|
||||
}
|
||||
|
||||
static inline int is_uuid32(const char *string)
|
||||
{
|
||||
return (strlen(string) == 8 || strlen(string) == 10);
|
||||
}
|
||||
|
||||
static inline int is_uuid16(const char *string)
|
||||
{
|
||||
return (strlen(string) == 4 || strlen(string) == 6);
|
||||
}
|
||||
|
||||
static int bt_string_to_uuid16(bt_uuid_t *uuid, const char *string)
|
||||
{
|
||||
uint16_t u16;
|
||||
char *endptr = NULL;
|
||||
|
||||
u16 = strtol(string, &endptr, 16);
|
||||
if (endptr && (*endptr == '\0' || *endptr == '-')) {
|
||||
bt_uuid16_create(uuid, u16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int bt_string_to_uuid32(bt_uuid_t *uuid, const char *string)
|
||||
{
|
||||
uint32_t u32;
|
||||
char *endptr = NULL;
|
||||
|
||||
u32 = strtol(string, &endptr, 16);
|
||||
if (endptr && *endptr == '\0') {
|
||||
bt_uuid32_create(uuid, u32);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int bt_string_to_uuid128(bt_uuid_t *uuid, const char *string)
|
||||
{
|
||||
uint32_t data0, data4;
|
||||
uint16_t data1, data2, data3, data5;
|
||||
uint128_t u128;
|
||||
uint8_t *val = (uint8_t *) &u128;
|
||||
|
||||
if (sscanf(string, "%08x-%04hx-%04hx-%04hx-%08x%04hx",
|
||||
&data0, &data1, &data2,
|
||||
&data3, &data4, &data5) != 6)
|
||||
return -EINVAL;
|
||||
|
||||
data0 = htonl(data0);
|
||||
data1 = htons(data1);
|
||||
data2 = htons(data2);
|
||||
data3 = htons(data3);
|
||||
data4 = htonl(data4);
|
||||
data5 = htons(data5);
|
||||
|
||||
memcpy(&val[0], &data0, 4);
|
||||
memcpy(&val[4], &data1, 2);
|
||||
memcpy(&val[6], &data2, 2);
|
||||
memcpy(&val[8], &data3, 2);
|
||||
memcpy(&val[10], &data4, 4);
|
||||
memcpy(&val[14], &data5, 2);
|
||||
|
||||
bt_uuid128_create(uuid, u128);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_string_to_uuid(bt_uuid_t *uuid, const char *string)
|
||||
{
|
||||
if (is_base_uuid128(string))
|
||||
return bt_string_to_uuid16(uuid, string + 4);
|
||||
else if (is_uuid128(string))
|
||||
return bt_string_to_uuid128(uuid, string);
|
||||
else if (is_uuid32(string))
|
||||
return bt_string_to_uuid32(uuid, string);
|
||||
else if (is_uuid16(string))
|
||||
return bt_string_to_uuid16(uuid, string);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int bt_uuid_strcmp(const void *a, const void *b)
|
||||
{
|
||||
return strcasecmp(a, b);
|
||||
}
|
||||
|
||||
int bt_uuid_to_le(const bt_uuid_t *src, void *dst)
|
||||
{
|
||||
bt_uuid_t uuid;
|
||||
|
||||
switch (src->type) {
|
||||
case BT_UUID16:
|
||||
bt_put_le16(src->value.u16, dst);
|
||||
return 0;
|
||||
case BT_UUID32:
|
||||
bt_uuid32_to_uuid128(src, &uuid);
|
||||
src = &uuid;
|
||||
/* Fallthrough */
|
||||
case BT_UUID128:
|
||||
/* Convert from 128-bit BE to LE */
|
||||
bswap_128(&src->value.u128, dst);
|
||||
return 0;
|
||||
case BT_UUID_UNSPEC:
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
181
uuid.h
Normal file
181
uuid.h
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2011 Nokia Corporation
|
||||
* Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __BLUETOOTH_UUID_H
|
||||
#define __BLUETOOTH_UUID_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define HSP_HS_UUID "00001108-0000-1000-8000-00805f9b34fb"
|
||||
#define HSP_AG_UUID "00001112-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define HFP_HS_UUID "0000111e-0000-1000-8000-00805f9b34fb"
|
||||
#define HFP_AG_UUID "0000111f-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define ADVANCED_AUDIO_UUID "0000110d-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define A2DP_SOURCE_UUID "0000110a-0000-1000-8000-00805f9b34fb"
|
||||
#define A2DP_SINK_UUID "0000110b-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define AVRCP_REMOTE_UUID "0000110e-0000-1000-8000-00805f9b34fb"
|
||||
#define AVRCP_TARGET_UUID "0000110c-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define PANU_UUID "00001115-0000-1000-8000-00805f9b34fb"
|
||||
#define NAP_UUID "00001116-0000-1000-8000-00805f9b34fb"
|
||||
#define GN_UUID "00001117-0000-1000-8000-00805f9b34fb"
|
||||
#define BNEP_SVC_UUID "0000000f-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define PNPID_UUID "00002a50-0000-1000-8000-00805f9b34fb"
|
||||
#define DEVICE_INFORMATION_UUID "0000180a-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define GATT_UUID "00001801-0000-1000-8000-00805f9b34fb"
|
||||
#define IMMEDIATE_ALERT_UUID "00001802-0000-1000-8000-00805f9b34fb"
|
||||
#define LINK_LOSS_UUID "00001803-0000-1000-8000-00805f9b34fb"
|
||||
#define TX_POWER_UUID "00001804-0000-1000-8000-00805f9b34fb"
|
||||
#define BATTERY_UUID "0000180f-0000-1000-8000-00805f9b34fb"
|
||||
#define SCAN_PARAMETERS_UUID "00001813-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define SAP_UUID "0000112D-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define HEART_RATE_UUID "0000180d-0000-1000-8000-00805f9b34fb"
|
||||
#define HEART_RATE_MEASUREMENT_UUID "00002a37-0000-1000-8000-00805f9b34fb"
|
||||
#define BODY_SENSOR_LOCATION_UUID "00002a38-0000-1000-8000-00805f9b34fb"
|
||||
#define HEART_RATE_CONTROL_POINT_UUID "00002a39-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define HEALTH_THERMOMETER_UUID "00001809-0000-1000-8000-00805f9b34fb"
|
||||
#define TEMPERATURE_MEASUREMENT_UUID "00002a1c-0000-1000-8000-00805f9b34fb"
|
||||
#define TEMPERATURE_TYPE_UUID "00002a1d-0000-1000-8000-00805f9b34fb"
|
||||
#define INTERMEDIATE_TEMPERATURE_UUID "00002a1e-0000-1000-8000-00805f9b34fb"
|
||||
#define MEASUREMENT_INTERVAL_UUID "00002a21-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define CYCLING_SC_UUID "00001816-0000-1000-8000-00805f9b34fb"
|
||||
#define CSC_MEASUREMENT_UUID "00002a5b-0000-1000-8000-00805f9b34fb"
|
||||
#define CSC_FEATURE_UUID "00002a5c-0000-1000-8000-00805f9b34fb"
|
||||
#define SENSOR_LOCATION_UUID "00002a5d-0000-1000-8000-00805f9b34fb"
|
||||
#define SC_CONTROL_POINT_UUID "00002a55-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define RFCOMM_UUID_STR "00000003-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define HDP_UUID "00001400-0000-1000-8000-00805f9b34fb"
|
||||
#define HDP_SOURCE_UUID "00001401-0000-1000-8000-00805f9b34fb"
|
||||
#define HDP_SINK_UUID "00001402-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define HID_UUID "00001124-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define DUN_GW_UUID "00001103-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define GAP_UUID "00001800-0000-1000-8000-00805f9b34fb"
|
||||
#define PNP_UUID "00001200-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define SPP_UUID "00001101-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define OBEX_SYNC_UUID "00001104-0000-1000-8000-00805f9b34fb"
|
||||
#define OBEX_OPP_UUID "00001105-0000-1000-8000-00805f9b34fb"
|
||||
#define OBEX_FTP_UUID "00001106-0000-1000-8000-00805f9b34fb"
|
||||
#define OBEX_PCE_UUID "0000112e-0000-1000-8000-00805f9b34fb"
|
||||
#define OBEX_PSE_UUID "0000112f-0000-1000-8000-00805f9b34fb"
|
||||
#define OBEX_PBAP_UUID "00001130-0000-1000-8000-00805f9b34fb"
|
||||
#define OBEX_MAS_UUID "00001132-0000-1000-8000-00805f9b34fb"
|
||||
#define OBEX_MNS_UUID "00001133-0000-1000-8000-00805f9b34fb"
|
||||
#define OBEX_MAP_UUID "00001134-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
/* GATT UUIDs section */
|
||||
#define GATT_PRIM_SVC_UUID 0x2800
|
||||
#define GATT_SND_SVC_UUID 0x2801
|
||||
#define GATT_INCLUDE_UUID 0x2802
|
||||
#define GATT_CHARAC_UUID 0x2803
|
||||
|
||||
/* GATT Characteristic Types */
|
||||
#define GATT_CHARAC_DEVICE_NAME 0x2A00
|
||||
#define GATT_CHARAC_APPEARANCE 0x2A01
|
||||
#define GATT_CHARAC_PERIPHERAL_PRIV_FLAG 0x2A02
|
||||
#define GATT_CHARAC_RECONNECTION_ADDRESS 0x2A03
|
||||
#define GATT_CHARAC_PERIPHERAL_PREF_CONN 0x2A04
|
||||
#define GATT_CHARAC_SERVICE_CHANGED 0x2A05
|
||||
#define GATT_CHARAC_SYSTEM_ID 0x2A23
|
||||
#define GATT_CHARAC_MODEL_NUMBER_STRING 0x2A24
|
||||
#define GATT_CHARAC_SERIAL_NUMBER_STRING 0x2A25
|
||||
#define GATT_CHARAC_FIRMWARE_REVISION_STRING 0x2A26
|
||||
#define GATT_CHARAC_HARDWARE_REVISION_STRING 0x2A27
|
||||
#define GATT_CHARAC_SOFTWARE_REVISION_STRING 0x2A28
|
||||
#define GATT_CHARAC_MANUFACTURER_NAME_STRING 0x2A29
|
||||
#define GATT_CHARAC_PNP_ID 0x2A50
|
||||
|
||||
/* GATT Characteristic Descriptors */
|
||||
#define GATT_CHARAC_EXT_PROPER_UUID 0x2900
|
||||
#define GATT_CHARAC_USER_DESC_UUID 0x2901
|
||||
#define GATT_CLIENT_CHARAC_CFG_UUID 0x2902
|
||||
#define GATT_SERVER_CHARAC_CFG_UUID 0x2903
|
||||
#define GATT_CHARAC_FMT_UUID 0x2904
|
||||
#define GATT_CHARAC_AGREG_FMT_UUID 0x2905
|
||||
#define GATT_CHARAC_VALID_RANGE_UUID 0x2906
|
||||
#define GATT_EXTERNAL_REPORT_REFERENCE 0x2907
|
||||
#define GATT_REPORT_REFERENCE 0x2908
|
||||
|
||||
typedef struct {
|
||||
enum {
|
||||
BT_UUID_UNSPEC = 0,
|
||||
BT_UUID16 = 16,
|
||||
BT_UUID32 = 32,
|
||||
BT_UUID128 = 128,
|
||||
} type;
|
||||
union {
|
||||
uint16_t u16;
|
||||
uint32_t u32;
|
||||
uint128_t u128;
|
||||
} value;
|
||||
} bt_uuid_t;
|
||||
|
||||
int bt_uuid_strcmp(const void *a, const void *b);
|
||||
|
||||
int bt_uuid16_create(bt_uuid_t *btuuid, uint16_t value);
|
||||
int bt_uuid32_create(bt_uuid_t *btuuid, uint32_t value);
|
||||
int bt_uuid128_create(bt_uuid_t *btuuid, uint128_t value);
|
||||
|
||||
int bt_uuid_cmp(const bt_uuid_t *uuid1, const bt_uuid_t *uuid2);
|
||||
void bt_uuid_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst);
|
||||
|
||||
#define MAX_LEN_UUID_STR 37
|
||||
|
||||
int bt_uuid_to_string(const bt_uuid_t *uuid, char *str, size_t n);
|
||||
int bt_string_to_uuid(bt_uuid_t *uuid, const char *string);
|
||||
|
||||
int bt_uuid_to_le(const bt_uuid_t *uuid, void *dst);
|
||||
|
||||
static inline int bt_uuid_len(const bt_uuid_t *uuid)
|
||||
{
|
||||
return uuid->type / 8;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __BLUETOOTH_UUID_H */
|
||||
Reference in New Issue
Block a user