summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac1
-rw-r--r--include/Makefile.am6
-rw-r--r--include/linux/dect.h206
-rw-r--r--include/linux/dect_netlink.h397
-rw-r--r--include/linux/netlink.h2
-rw-r--r--include/netlink-private/types.h134
-rw-r--r--include/netlink/cli/utils.h6
-rw-r--r--include/netlink/dect/ari.h39
-rw-r--r--include/netlink/dect/cell.h61
-rw-r--r--include/netlink/dect/cluster.h61
-rw-r--r--include/netlink/dect/dect.h6
-rw-r--r--include/netlink/dect/llme.h94
-rw-r--r--include/netlink/dect/transceiver.h57
-rw-r--r--include/netlink/netlink.h1
-rw-r--r--lib/Makefile.am8
-rw-r--r--lib/dect/ari.c245
-rw-r--r--lib/dect/cell.c267
-rw-r--r--lib/dect/cell_obj.c234
-rw-r--r--lib/dect/cluster.c417
-rw-r--r--lib/dect/cluster_obj.c274
-rw-r--r--lib/dect/dect.c22
-rw-r--r--lib/dect/llme.c776
-rw-r--r--lib/dect/transceiver.c307
-rw-r--r--lib/dect/transceiver_obj.c335
-rw-r--r--libnl-dect-3.0.pc.in11
-rw-r--r--src/.gitignore9
-rw-r--r--src/Makefile.am24
-rw-r--r--src/dect-cell-add.c67
-rw-r--r--src/dect-cell-delete.c59
-rw-r--r--src/dect-cell-list.c24
-rw-r--r--src/dect-cluster-add.c95
-rw-r--r--src/dect-cluster-delete.c64
-rw-r--r--src/dect-cluster-list.c20
-rw-r--r--src/dect-llme-mac-con.c111
-rw-r--r--src/dect-llme-monitor.c37
-rw-r--r--src/dect-llme-scan.c139
-rw-r--r--src/dect-transceiver-bind.c62
-rw-r--r--src/dect-transceiver-list.c60
38 files changed, 4735 insertions, 3 deletions
diff --git a/configure.ac b/configure.ac
index 744e34e..7797c3d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -108,6 +108,7 @@ libnl-3.0.pc
libnl-route-3.0.pc
libnl-genl-3.0.pc
libnl-nf-3.0.pc
+libnl-dect-3.0.pc
libnl-cli-3.0.pc
lib/Makefile
include/Makefile
diff --git a/include/Makefile.am b/include/Makefile.am
index 3488c52..e4fe6a4 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -17,6 +17,12 @@ nobase_libnlinclude_HEADERS = \
netlink/netfilter/nfnl.h \
netlink/netfilter/queue.h \
netlink/netfilter/queue_msg.h \
+ netlink/dect/ari.h \
+ netlink/dect/cell.h \
+ netlink/dect/cluster.h \
+ netlink/dect/dect.h \
+ netlink/dect/llme.h \
+ netlink/dect/transceiver.h \
netlink/addr.h \
netlink/attr.h \
netlink/cache.h \
diff --git a/include/linux/dect.h b/include/linux/dect.h
new file mode 100644
index 0000000..e458a9c
--- /dev/null
+++ b/include/linux/dect.h
@@ -0,0 +1,206 @@
+#ifndef _LINUX_DECT_H
+#define _LINUX_DECT_H
+
+#define DECTNAMSIZ 16
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* these have to be macros in order to be usable for module aliases */
+#define DECT_RAW 0 /* raw frames */
+#define DECT_B_SAP 1 /* DLC Broadcast Service */
+#define DECT_S_SAP 2 /* DLC Data Link Service */
+#define DECT_LU1_SAP 3 /* LU1 sockets */
+#define DECT_PROTO_NUM 4
+
+/**
+ * struct sockaddr_dect
+ *
+ * @dect_family: address family (AF_DECT)
+ * @dect_index: cluster index
+ */
+struct sockaddr_dect {
+ sa_family_t dect_family;
+ int dect_index;
+};
+
+/* raw sockets */
+
+#define DECT_RAW_AUXDATA 0
+
+/**
+ * struct dect_raw_auxdata - raw socket auxiliary frame data
+ *
+ * @mfn: multi-frame number
+ * @frame: frame number
+ * @slot: slot numer
+ * @rssi: receive signal strength indicator
+ */
+struct dect_raw_auxdata {
+ __u32 mfn;
+ __u8 frame;
+ __u8 slot;
+ __u8 rssi;
+};
+
+#define DECT_BSAP_AUXDATA 0
+
+/**
+ * struct dect_bsap_auxdata
+ *
+ * @long_page: message contains a long page
+ */
+struct dect_bsap_auxdata {
+ __u8 long_page;
+};
+
+/**
+ * enum dect_sapis - S SAP Identifier
+ *
+ * @DECT_SAPI_CO_SIGNALLING: connection oriented signalling
+ * @DECT_SAPI_CL_SIGNALLING: connectionless signalling
+ * @DECT_SAPI_ANY: wildcard
+ */
+enum dect_sapis {
+ DECT_SAPI_CO_SIGNALLING = 0,
+ DECT_SAPI_CL_SIGNALLING = 3,
+ DECT_SAPI_ANY = 7,
+};
+
+/**
+ * enum dect_llns - Logical Link Numbers
+ *
+ * @DECT_LLN_CLASS_U: Class U operation
+ * @DECT_LLN_CLASS_A: Class A operation
+ * @DECT_LLN_ASSIGNABLE*: Assignable LLN (class B operation)
+ * @DECT_LLN_UNASSIGNED: LLN unassigned (class B operation
+ * @DECT_LLN_ANY: wildcard
+ */
+enum dect_llns {
+ DECT_LLN_CLASS_U = 0,
+ DECT_LLN_CLASS_A = 1,
+ DECT_LLN_ASSIGNABLE_MIN = 2,
+ DECT_LLN_ASSIGNABLE_MAX = 6,
+ DECT_LLN_UNASSIGNED = 7,
+ DECT_LLN_ANY = 15,
+};
+
+/**
+ * struct sockaddr_dect_ssap
+ *
+ * @dect_family: family (AF_DECT)
+ * @dect_lln: logical link number
+ * @dect_sapi: service access point identifier
+ * @dect_class: class A/B
+ * @dect_index: cluster index
+ * @dect_ari: ARI
+ * @dect_pmid: PMID
+ * @dect_lcn: logical connection number
+ */
+struct sockaddr_dect_ssap {
+ sa_family_t dect_family;
+ __u8 dect_lln:4,
+ dect_sapi:3;
+ __u8 dect_class;
+ int dect_index;
+ __u64 dect_ari:40,
+ dect_pmid:20,
+ dect_lcn:3;
+};
+
+/* S-SAP primitives */
+#define DECT_DL_ENC_KEY 1
+#define DECT_DL_ENCRYPT 2
+#define DECT_DL_MAC_CONN_PARAMS 3
+
+enum dect_cipher_states {
+ DECT_CIPHER_DISABLED,
+ DECT_CIPHER_ENABLED,
+};
+
+/**
+ * enum dect_mac_connection_types - MAC Connection types
+ *
+ * @DECT_MAC_CONN_BASIC: Basic connection, always I_N_min_delay service
+ * @DECT_MAC_CONN_ADVANCED: Advanced connection
+ * @DECT_MAC_CONN_COMPLEMENT: Complementary connection
+ */
+enum dect_mac_connection_types {
+ DECT_MAC_CONN_BASIC,
+ DECT_MAC_CONN_ADVANCED,
+ DECT_MAC_CONN_COMPLEMENT,
+};
+
+enum dect_mac_service_types {
+ DECT_SERVICE_IN_MIN_DELAY = 0x0,
+ DECT_SERVICE_IPX_ENCODED_PROTECTED = 0x1,
+ DECT_SERVICE_IN_NORMAL_DELAY = 0x2,
+ DECT_SERVICE_UNKNOWN = 0x4,
+ DECT_SERVICE_C_CHANNEL_ONLY = 0x5,
+ DECT_SERVICE_IP_ERROR_DETECTION = 0x10,
+ DECT_SERVICE_IPQ_ERROR_DETECTION = 0x14,
+ /* Lifetime encoded in low three bits */
+ DECT_SERVICE_IP_ERROR_CORRECTION = 0x18,
+ DECT_SERVICE_IPQ_ERROR_CORRECTION = 0x38,
+};
+
+/**
+ * enum dect_slot_types - DECT slot types
+ *
+ * @DECT_FULL_SLOT: Full-slot format (480 bits)
+ * @DECT_HALF_SLOT: Half-slot format (240 bits)
+ * @DECT_DOUBLE_SLOT: Double-slot format (960 bits)
+ * @DECT_LONG_SLOT_j640: Long slot format j=640 (800 bits)
+ * @DECT_LONG_SLOT_j672: Long slot format j=672 (832 bits)
+ *
+ * The numeric values must match the MAC-layer attributes-T coding.
+ */
+enum dect_slot_types {
+ DECT_FULL_SLOT = 0x0,
+ DECT_HALF_SLOT = 0x1,
+ DECT_DOUBLE_SLOT = 0x2,
+ DECT_LONG_SLOT_640 = 0x3,
+ DECT_LONG_SLOT_672 = 0x4,
+};
+
+struct dect_mac_conn_params {
+ enum dect_mac_connection_types type;
+ enum dect_mac_service_types service;
+ enum dect_slot_types slot;
+};
+
+/**
+ * struct dect_dl_encrypt - DL_ENCRYPT primitive arguments
+ *
+ * @status: desired/achieved encryption status
+ */
+struct dect_dl_encrypt {
+ enum dect_cipher_states status;
+};
+
+/**
+ * struct sockaddr_dect_lu - DLC U-plane LUx service instance address
+ *
+ * @dect_family: address family (AF_DECT)
+ * @dect_mci: MAC Connection Identifier
+ */
+struct sockaddr_dect_lu {
+ sa_family_t dect_family;
+ int dect_index;
+ __u64 dect_ari:40,
+ dect_pmid:20,
+ dect_lcn:3;
+};
+
+/* LU1 SAP */
+
+#define DECT_LU1_QUEUE_STATS 0
+
+struct dect_lu1_queue_stats {
+ __u32 rx_bytes;
+ __u32 rx_underflow;
+ __u32 tx_bytes;
+ __u32 tx_underflow;
+};
+
+#endif /* _LINUX_DECT_H */
diff --git a/include/linux/dect_netlink.h b/include/linux/dect_netlink.h
new file mode 100644
index 0000000..7c01aca
--- /dev/null
+++ b/include/linux/dect_netlink.h
@@ -0,0 +1,397 @@
+#ifndef _LINUX_DECT_NETLINK_H
+#define _LINUX_DECT_NETLINK_H
+
+struct dectmsg {
+ int dm_index;
+};
+
+enum dect_nlgroups {
+ DECTNLGRP_NONE,
+ DECTNLGRP_TRANSCEIVER,
+ DECTNLGRP_CELL,
+ DECTNLGRP_CLUSTER,
+ DECTNLGRP_LLME,
+ __DECTNLGRP_MAX
+};
+#define DECTNLGRP_MAX (__DECTNLGRP_MAX - 1)
+
+enum dect_netlink_msg_types {
+ DECT_MSG_BASE = 0x10,
+ DECT_NEW_TRANSCEIVER,
+ DECT_DEL_TRANSCEIVER,
+ DECT_GET_TRANSCEIVER,
+ DECT_NEW_CELL,
+ DECT_DEL_CELL,
+ DECT_GET_CELL,
+ DECT_NEW_CLUSTER,
+ DECT_DEL_CLUSTER,
+ DECT_GET_CLUSTER,
+ DECT_LLME_MSG,
+ __DECT_MSG_MAX
+};
+#define DECT_MSG_MAX (__DECT_MSG_MAX - 1)
+
+#define DECT_NR_MSGTYPES (DECT_MSG_MAX + 1 - DECT_MSG_BASE)
+
+enum dect_list_attrs {
+ DECTA_LIST_UNSPEC,
+ DECTA_LIST_ELEM,
+ __DECTA_LIST_MAX
+};
+#define DECTA_LIST_MAX (__DECTA_LIST_MAX - 1)
+
+enum dect_slot_states {
+ DECT_SLOT_IDLE,
+ DECT_SLOT_SCANNING,
+ DECT_SLOT_RX,
+ DECT_SLOT_TX,
+};
+
+enum dect_slot_flags {
+ DECT_SLOT_SYNC = 0x1,
+ DECT_SLOT_CIPHER = 0x2,
+};
+
+/**
+ * enum dect_packet_types - DECT Physical Packet Types
+ *
+ * @DECT_PACKET_P00: short physical packet P00, 96 bits, A-field only
+ * @DECT_PACKET_P08: low capacity physical packet P08j, 180 bits
+ * @DECT_PACKET_P32: basic physical packet P32, 420 bits
+ * @DECT_PACKET_P80: high capacity physical packet P80, 900 bits
+ * @DECT_PACKET_P640j: variable capacity packet P640j, 712 bits
+ * @DECT_PACKET_P672j: variable capacity packet P640j, 744 bits
+ */
+enum dect_packet_types {
+ DECT_PACKET_P00,
+ DECT_PACKET_P08,
+ DECT_PACKET_P32,
+ DECT_PACKET_P80,
+ DECT_PACKET_P640j,
+ DECT_PACKET_P672j,
+ __DECT_PACKET_MAX
+};
+#define DECT_PACKET_MAX (__DECT_PACKET_MAX - 1)
+
+#define DECT_PHASE_OFFSET_SCALE 1024
+
+enum dect_slot_attrs {
+ DECTA_SLOT_UNSPEC,
+ DECTA_SLOT_NUM,
+ DECTA_SLOT_STATE,
+ DECTA_SLOT_FLAGS,
+ DECTA_SLOT_PACKET,
+ DECTA_SLOT_CARRIER,
+ DECTA_SLOT_FREQUENCY,
+ DECTA_SLOT_PHASEOFF,
+ DECTA_SLOT_RSSI,
+ DECTA_SLOT_RX_PACKETS,
+ DECTA_SLOT_RX_BYTES,
+ DECTA_SLOT_RX_A_CRC_ERRORS,
+ DECTA_SLOT_RX_X_CRC_ERRORS,
+ DECTA_SLOT_RX_Z_CRC_ERRORS,
+ DECTA_SLOT_TX_PACKETS,
+ DECTA_SLOT_TX_BYTES,
+ __DECTA_SLOT_MAX
+};
+#define DECTA_SLOT_MAX (__DECTA_SLOT_MAX - 1)
+
+enum dect_transceiver_stats_attrs {
+ DECTA_TRANSCEIVER_STATS_UNSPEC,
+ DECTA_TRANSCEIVER_STATS_EVENT_BUSY,
+ DECTA_TRANSCEIVER_STATS_EVENT_LATE,
+ __DECTA_TRANSCEIVER_STATS_MAX
+};
+#define DECTA_TRANSCEIVER_STATS_MAX (__DECTA_TRANSCEIVER_STATS_MAX - 1)
+
+/**
+ * @DECT_TRANSCEIVER_SLOW_HOPPING: transceiver has slow hopping radio
+ * @DECT_TRANSCEIVER_PACKET_P64: transceiver supports packet P640j
+ */
+enum dect_transceiver_features {
+ DECT_TRANSCEIVER_SLOW_HOPPING = 0x1,
+ DECT_TRANSCEIVER_PACKET_P64 = 0x2,
+};
+
+enum dect_transceiver_attrs {
+ DECTA_TRANSCEIVER_UNSPEC,
+ DECTA_TRANSCEIVER_NAME,
+ DECTA_TRANSCEIVER_TYPE,
+ DECTA_TRANSCEIVER_FEATURES,
+ DECTA_TRANSCEIVER_LINK,
+ DECTA_TRANSCEIVER_STATS,
+ DECTA_TRANSCEIVER_BAND,
+ DECTA_TRANSCEIVER_SLOTS,
+ __DECTA_TRANSCEIVER_MAX
+};
+#define DECTA_TRANSCEIVER_MAX (__DECTA_TRANSCEIVER_MAX - 1)
+
+enum dect_cell_flags {
+ DECT_CELL_CCP = (1 << 0),
+ DECT_CELL_SLAVE = (1 << 1),
+ DECT_CELL_MONITOR = (1 << 2),
+};
+
+enum dect_cell_attrs {
+ DECTA_CELL_UNSPEC,
+ DECTA_CELL_NAME,
+ DECTA_CELL_FLAGS,
+ DECTA_CELL_TRANSCEIVERS,
+ DECTA_CELL_CLUSTER,
+ __DECTA_CELL_MAX
+};
+#define DECTA_CELL_MAX (__DECTA_CELL_MAX - 1)
+
+enum dect_mbc_state {
+ DECT_MBC_NONE,
+ DECT_MBC_INITIATED,
+ DECT_MBC_ESTABLISHED,
+ DECT_MBC_RELEASED,
+};
+
+enum dect_mbc_tb_attrs {
+ DECTA_MBC_TB_UNSPEC,
+ DECTA_MBC_TB_LBN,
+ DECTA_MBC_TB_ECN,
+ DECTA_MBC_TB_CELL,
+ DECTA_MBC_TB_RX_SLOT,
+ DECTA_MBC_TB_TX_SLOT,
+ __DECTA_MBC_TB_MAX,
+};
+#define DECTA_MBC_TB_MAX (__DECTA_MBC_TB_MAX - 1)
+
+enum dect_mbc_stats_attrs {
+ DECTA_MBC_STATS_UNSPEC,
+ DECTA_MBC_STATS_CS_RX_BYTES,
+ DECTA_MBC_STATS_CS_TX_BYTES,
+ DECTA_MBC_STATS_I_RX_BYTES,
+ DECTA_MBC_STATS_I_TX_BYTES,
+ DECTA_MBC_STATS_HANDOVERS,
+ __DECTA_MBC_STATS_MAX,
+};
+#define DECTA_MBC_STATS_MAX (__DECTA_MBC_STATS_MAX - 1)
+
+enum dect_mbc_attrs {
+ DECTA_MBC_UNSPEC,
+ DECTA_MBC_MCEI,
+ DECTA_MBC_SERVICE,
+ DECTA_MBC_STATE,
+ DECTA_MBC_CIPHER_STATE,
+ DECTA_MBC_STATS,
+ DECTA_MBC_TBS,
+ __DECTA_MBC_MAX,
+};
+#define DECTA_MBC_MAX (__DECTA_MBC_MAX - 1)
+
+enum dect_cluster_attrs {
+ DECTA_CLUSTER_UNSPEC,
+ DECTA_CLUSTER_NAME,
+ DECTA_CLUSTER_MODE,
+ DECTA_CLUSTER_PARI,
+ DECTA_CLUSTER_CELLS,
+ DECTA_CLUSTER_MBCS,
+ __DECTA_CLUSTER_MAX
+};
+#define DECTA_CLUSTER_MAX (__DECTA_CLUSTER_MAX - 1)
+
+enum dect_cluster_modes {
+ DECT_MODE_FP,
+ DECT_MODE_PP,
+};
+
+/**
+ * DECT ARI classes
+ *
+ * @DECT_ARC_A: Residential and private (PBX) single- and small multiple cell systems
+ * @DECT_ARC_B: Private (PABXs) multiple cell
+ * @DECT_ARC_C: Public single and multiple cell systems
+ * @DECT_ARC_D: Public DECT access to a GSM network
+ * @DECT_ARC_E: PP to PP direct communication (private)
+ */
+enum dect_ari_classes {
+ DECT_ARC_A,
+ DECT_ARC_B,
+ DECT_ARC_C,
+ DECT_ARC_D,
+ DECT_ARC_E,
+};
+
+enum dect_ari_attrs {
+ DECTA_ARI_UNSPEC,
+ DECTA_ARI_CLASS,
+ DECTA_ARI_FPN,
+ DECTA_ARI_FPS,
+ DECTA_ARI_EMC,
+ DECTA_ARI_EIC,
+ DECTA_ARI_POC,
+ DECTA_ARI_GOP,
+ DECTA_ARI_FIL,
+ __DECTA_ARI_MAX
+};
+#define DECTA_ARI_MAX (__DECTA_ARI_MAX - 1)
+
+enum decta_sari_attrs {
+ DECTA_SARI_UNSPEC,
+ DECTA_SARI_ARI,
+ DECTA_SARI_BLACK,
+ DECTA_SARI_TARI,
+ __DECTA_SARI_MAX
+};
+#define DECTA_SARI_MAX (__DECTA_SARI_MAX - 1)
+
+enum dect_fixed_part_capabilities {
+ DECT_FPC_EXTENDED_FP_INFO = 0x80000,
+ DECT_FPC_DOUBLE_DUPLEX_BEARER_CONNECTION= 0x40000,
+ DECT_FPC_RESERVED = 0x20000,
+ DECT_FPC_DOUBLE_SLOT = 0x10000,
+ DECT_FPC_HALF_SLOT = 0x08000,
+ DECT_FPC_FULL_SLOT = 0x04000,
+ DECT_FPC_FREQ_CONTROL = 0x02000,
+ DECT_FPC_PAGE_REPETITION = 0x01000,
+ DECT_FPC_CO_SETUP_ON_DUMMY = 0x00800,
+ DECT_FPC_CL_UPLINK = 0x00400,
+ DECT_FPC_CL_DOWNLINK = 0x00200,
+ DECT_FPC_BASIC_A_FIELD_SETUP = 0x00100,
+ DECT_FPC_ADV_A_FIELD_SETUP = 0x00080,
+ DECT_FPC_B_FIELD_SETUP = 0x00040,
+ DECT_FPC_CF_MESSAGES = 0x00020,
+ DECT_FPC_IN_MIN_DELAY = 0x00010,
+ DECT_FPC_IN_NORM_DELAY = 0x00008,
+ DECT_FPC_IP_ERROR_DETECTION = 0x00004,
+ DECT_FPC_IP_ERROR_CORRECTION = 0x00002,
+ DECT_FPC_MULTIBEARER_CONNECTIONS = 0x00001,
+};
+
+enum dect_higher_layer_capabilities {
+ DECT_HLC_ADPCM_G721_VOICE = 0x8000,
+ DECT_HLC_GAP_PAP_BASIC_SPEECH = 0x4000,
+ DECT_HLC_NON_VOICE_CIRCUIT_SWITCHED = 0x2000,
+ DECT_HLC_NON_VOICE_PACKET_SWITCHED = 0x1000,
+ DECT_HLC_STANDARD_AUTHENTICATION = 0x0800,
+ DECT_HLC_STANDARD_CIPHERING = 0x0400,
+ DECT_HLC_LOCATION_REGISTRATION = 0x0200,
+ DECT_HLC_SIM_SERVICES = 0x0100,
+ DECT_HLC_NON_STATIC_FIXED_PART = 0x0080,
+ DECT_HLC_CISS_SERVICE = 0x0040,
+ DECT_HLC_CLMS_SERVICE = 0x0020,
+ DECT_HLC_COMS_SERVICE = 0x0010,
+ DECT_HLC_ACCESS_RIGHTS_REQUESTS = 0x0008,
+ DECT_HLC_EXTERNAL_HANDOVER = 0x0004,
+ DECT_HLC_CONNECTION_HANDOVER = 0x0002,
+ DECT_HLC_RESERVED = 0x0001,
+};
+
+enum dect_extended_fixed_part_capabilities {
+ DECT_EFPC_WRS_MASK = 0x1f80,
+ DECT_EFPC_WRS_CRFP_HOPS_MASK = 0x1800,
+ DECT_EFPC_WRS_CRFP_HOPS_1 = 0x0000,
+ DECT_EFPC_WRS_CRFP_HOPS_2 = 0x0800,
+ DECT_EFPC_WRS_CRFP_HOPS_3 = 0x1000,
+ DECT_EFPC_WRS_CRFP_HOPS_NONE = 0x1800,
+ DECT_EFPC_WRS_CRFP_ENCRYPTION = 0x0400,
+ DECT_EFPC_WRS_REP_HOPS_MASK = 0x0300,
+ DECT_EFPC_WRS_REP_HOPS_NONE = 0x0000,
+ DECT_EFPC_WRS_REP_HOPS_1 = 0x0100,
+ DECT_EFPC_WRS_REP_HOPS_2 = 0x0200,
+ DECT_EFPC_WRS_REP_HOPS_3 = 0x0300,
+ DECT_EFPC_WRS_REP_INTERLACING = 0x0080,
+ DECT_EFPC_SYNC_MASK = 0x0060,
+ DECT_EFPC_SYNC_PROLONGED_PREAMBLE = 0x0020,
+ DECT_EFPC_SYNC_RESERVED1 = 0x0010,
+ DECT_EFPC_MAC_SUSPEND_RESUME = 0x0008,
+ DECT_EFPC_MAC_IP_Q_SERVICE = 0x0004,
+ DECT_EFPC_EXTENDED_FP_INFO2 = 0x0002,
+ DECT_EFPC_RESERVED2 = 0x0001,
+};
+
+enum dect_extended_higher_layer_capabilities {
+ DECT_EHLC_ISDN_DATA_SERVICE = 0x000001,
+ DECT_EHLC_DPRS_FREL = 0x000002,
+ DECT_EHLC_DPRS_STREAM = 0x000004,
+ DECT_EHLC_DATA_SERVICE_PROFILE_D = 0x000008,
+ DECT_EHLC_LRMS = 0x000010,
+ DECT_EHLC_ASYMETRIC_BEARERS = 0x000040,
+ DECT_EHLC_EMERGENCY_CALLS = 0x000080,
+ DECT_EHLC_TPUI_LOCATION_REGISTRATION = 0x000100,
+ DECT_EHLC_GPS_SYNCHRONIZED = 0x000200,
+ DECT_EHLC_ISDN_INTERMEDIATE_SYSTEM = 0x000400,
+ DECT_EHLC_RAP_PART_1_PROFILE = 0x000800,
+ DECT_EHLC_V_24 = 0x004000,
+ DECT_EHLC_PPP = 0x008000,
+ DECT_EHLC_IP = 0x010000,
+ DECT_EHLC_TOKEN_RING = 0x020000,
+ DECT_EHLC_ETHERNET = 0x040000,
+ DECT_EHLC_IP_ROAMING = 0x080000,
+ DECT_EHLC_GENERIC_MEDIA_ENCAPSULATION = 0x100000,
+ DECT_EHLC_BASIC_ODAP = 0x200000,
+ DECT_EHLC_F_MMS_INTERWORKING_PROFILE = 0x400000,
+};
+
+enum dect_extended_fixed_part_capabilities2 {
+ DECT_EFPC2_LONG_SLOT_J640 = 0x800,
+ DECT_EFPC2_LONG_SLOT_J672 = 0x400,
+ DECT_EFPC2_IP_F = 0x200,
+ DECT_EFPC2_SI_PF = 0x100,
+ DECT_EFPC2_GF = 0x080,
+ DECT_EFPC2_NO_EMISSION_CARRIER = 0x001,
+};
+
+enum dect_extended_higher_layer_capabilities2 {
+ DECT_EHLC2_NG_DECT_PERMANENT_CLIR = 0x000100,
+ DECT_EHLC2_NG_DECT_MULTIPLE_CALLS = 0x000200,
+ DECT_EHLC2_NG_DECT_MULTIPLE_LINES = 0x000400,
+ DECT_EHLC2_EASY_PAIRING = 0x000800,
+ DECT_EHLC2_LIST_ACCESS_FEATURES = 0x001000,
+ DECT_EHLC2_NO_EMISSION_MODE = 0x002000,
+ DECT_EHLC2_NG_DECT_CALL_DEFLECTION = 0x004000,
+ DECT_EHLC2_NG_DECT_INTRUSION_CALL = 0x008000,
+ DECT_EHLC2_NG_DECT_CONFERENCE_CALL = 0x010000,
+ DECT_EHLC2_NG_DECT_PARALLEL_CALLS = 0x020000,
+ DECT_EHLC2_NG_DECT_CALL_TRANSFER = 0x040000,
+ DECT_EHLC2_NG_DECT_EXTENDED_WIDEBAND = 0x080000,
+ DECT_EHLC2_PACKET_DATA_CATEGORY_MASK = 0x700000,
+ DECT_EHLC2_NG_DECT_WIDEBAND = 0x800000,
+};
+
+enum dect_mac_info_attrs {
+ DECTA_MAC_INFO_UNSPEC,
+ DECTA_MAC_INFO_PARI,
+ DECTA_MAC_INFO_RPN,
+ DECTA_MAC_INFO_RSSI,
+ DECTA_MAC_INFO_SARI_LIST,
+ DECTA_MAC_INFO_FPC,
+ DECTA_MAC_INFO_HLC,
+ DECTA_MAC_INFO_EFPC,
+ DECTA_MAC_INFO_EHLC,
+ DECTA_MAC_INFO_EFPC2,
+ DECTA_MAC_INFO_EHLC2,
+ DECTA_MAC_INFO_MFN,
+ __DECTA_MAC_INFO_MAX
+};
+#define DECTA_MAC_INFO_MAX (__DECTA_MAC_INFO_MAX - 1)
+
+enum dect_llme_ops {
+ DECT_LLME_REQUEST,
+ DECT_LLME_INDICATE,
+ DECT_LLME_RESPONSE,
+ DECT_LLME_CONFIRM,
+};
+
+enum dect_llme_msg_types {
+ DECT_LLME_SCAN,
+ DECT_LLME_MAC_INFO,
+ DECT_LLME_MAC_RFP_PRELOAD,
+ __DECT_LLME_MAX
+};
+#define DECT_LLME_MAX (__DECT_LLME_MAX - 1)
+
+enum dect_llme_msg_attrs {
+ DECTA_LLME_UNSPEC,
+ DECTA_LLME_OP,
+ DECTA_LLME_TYPE,
+ DECTA_LLME_DATA,
+ __DECTA_LLME_MAX
+};
+#define DECTA_LLME_MAX (__DECTA_LLME_MAX - 1)
+
+#endif /* _LINUX_DECT_NETLINK_H */
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index 3925254..d427fcc 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -25,6 +25,8 @@
#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */
#define NETLINK_ECRYPTFS 19
#define NETLINK_RDMA 20
+#define NETLINK_CRYPTO 21 /* Crypto layer */
+#define NETLINK_DECT 22 /* DECT */
#define MAX_LINKS 32
diff --git a/include/netlink-private/types.h b/include/netlink-private/types.h
index 89f6418..6e2fa68 100644
--- a/include/netlink-private/types.h
+++ b/include/netlink-private/types.h
@@ -903,6 +903,140 @@ struct nfnl_queue_msg {
uint32_t queue_msg_verdict;
};
+struct nl_dect_transceiver_slot {
+ uint8_t dts_valid;
+ uint8_t dts_state;
+ uint32_t dts_flags;
+ uint8_t dts_packet;
+ uint8_t dts_carrier;
+ uint8_t dts_rssi;
+ uint32_t dts_frequency;
+ int32_t dts_phaseoff;
+ uint32_t dts_rx_bytes;
+ uint32_t dts_rx_packets;
+ uint32_t dts_rx_a_crc_errors;
+ uint32_t dts_rx_x_crc_errors;
+ uint32_t dts_rx_z_crc_errors;
+ uint32_t dts_tx_bytes;
+ uint32_t dts_tx_packets;
+};
+
+struct nl_dect_transceiver_stats {
+ uint32_t trx_event_busy;
+ uint32_t trx_event_late;
+};
+
+struct nl_dect_transceiver {
+ NLHDR_COMMON
+
+ char *trx_name;
+ char *trx_type;
+ uint32_t trx_features;
+ uint8_t trx_index;
+ uint8_t trx_link;
+ uint8_t trx_band;
+ struct nl_dect_transceiver_stats trx_stats;
+ struct nl_dect_transceiver_slot trx_slots[24];
+};
+
+struct nl_dect_ari {
+ uint32_t ari_flags;
+
+ uint8_t ari_class;
+ uint32_t ari_fpn;
+ uint32_t ari_fps;
+ union {
+ uint16_t emc;
+ uint16_t eic;
+ uint16_t poc;
+ uint32_t gop;
+ uint16_t fil;
+ } ari_u;
+};
+
+struct nl_dect_mbc_tb {
+ uint8_t mtb_lbn;
+ uint8_t mtb_ecn;
+ uint8_t mtb_cell;
+ uint8_t mtb_rx_slot;
+ uint8_t mtb_tx_slot;
+};
+
+struct nl_dect_mbc {
+ uint32_t mbc_mcei;
+ uint8_t mbc_state;
+ uint8_t mbc_service;
+ uint8_t mbc_cipher_state;
+ uint8_t mbc_ntbs;
+ struct nl_dect_mbc_tb mbc_tbs[8];
+
+ uint32_t mbc_cs_rx_bytes;
+ uint32_t mbc_cs_tx_bytes;
+ uint32_t mbc_i_rx_bytes;
+ uint32_t mbc_i_tx_bytes;
+ uint32_t mbc_handovers;
+};
+
+struct nl_dect_cluster {
+ NLHDR_COMMON
+
+ int cl_index;
+ char *cl_name;
+ uint8_t cl_mode;
+ struct nl_dect_ari cl_pari;
+ uint8_t cells[8];
+ struct nl_dect_mbc cl_mbcs[8];
+ uint8_t cl_nmbcs;
+};
+
+struct nl_dect_cell {
+ NLHDR_COMMON
+
+ int c_index;
+ char * c_name;
+ uint32_t c_flags;
+ char *c_transceiver[16];
+ int c_link;
+};
+
+struct nl_dect_llme_mac_info {
+ NLHDR_COMMON
+
+ struct nl_dect_ari mi_pari;
+ uint8_t mi_rpn;
+ uint8_t mi_rssi;
+ uint32_t mi_fpc;
+ uint16_t mi_hlc;
+ uint16_t mi_efpc;
+ uint32_t mi_ehlc;
+ uint16_t mi_efpc2;
+ uint32_t mi_ehlc2;
+ uint32_t mi_mfn;
+};
+
+struct nl_dect_llme_mac_con {
+ NLHDR_COMMON
+
+ uint32_t mc_mcei;
+ struct nl_dect_ari mc_ari;
+ uint32_t mc_pmid;
+ uint8_t mc_type;
+ uint8_t mc_ecn;
+ uint8_t mc_service;
+};
+
+struct nl_dect_llme_msg {
+ NLHDR_COMMON
+
+ int lm_index;
+ uint8_t lm_type;
+ uint8_t lm_op;
+ union {
+ struct nl_dect_llme_mac_info lm_mi;
+ struct nl_dect_llme_mac_con lm_mc;
+ };
+};
+
struct ematch_quoted {
char * data;
size_t len;
diff --git a/include/netlink/cli/utils.h b/include/netlink/cli/utils.h
index da41c10..cd0df83 100644
--- a/include/netlink/cli/utils.h
+++ b/include/netlink/cli/utils.h
@@ -22,6 +22,7 @@
#include <stdint.h>
#include <ctype.h>
#include <getopt.h>
+#include <limits.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/socket.h>
@@ -47,6 +48,11 @@
#include <netlink/genl/ctrl.h>
#include <netlink/genl/mngt.h>
#include <netlink/netfilter/ct.h>
+#include <netlink/dect/transceiver.h>
+#include <netlink/dect/cell.h>
+#include <netlink/dect/cluster.h>
+#include <netlink/dect/llme.h>
+#include <netlink/dect/ari.h>
#ifdef __cplusplus
extern "C" {
diff --git a/include/netlink/dect/ari.h b/include/netlink/dect/ari.h
new file mode 100644
index 0000000..76c7813
--- /dev/null
+++ b/include/netlink/dect/ari.h
@@ -0,0 +1,39 @@
+#ifndef NETLINK_DECT_ARI_H
+#define NETLINK_DECT_ARI_H
+
+#include <stdbool.h>
+
+struct nl_dect_ari;
+
+extern int nl_dect_parse_ari(struct nl_dect_ari *,
+ struct nlattr *);
+extern int nl_dect_fill_ari(struct nl_msg *,
+ const struct nl_dect_ari *,
+ int);
+extern void nl_dect_dump_ari(const struct nl_dect_ari *,
+ struct nl_dump_params *);
+
+extern const char * nl_dect_ari_class2str(enum dect_ari_classes,
+ char *buf, size_t len);
+extern enum dect_ari_classes nl_dect_ari_str2class(const char *);
+
+extern void nl_dect_ari_set_class(struct nl_dect_ari *,
+ enum dect_ari_classes);
+extern enum dect_ari_classes nl_dect_ari_get_class(const struct nl_dect_ari *);
+
+extern void nl_dect_ari_set_fpn(struct nl_dect_ari *, uint32_t);
+extern uint32_t nl_dect_ari_get_fpn(const struct nl_dect_ari *);
+extern void nl_dect_ari_set_fps(struct nl_dect_ari *, uint32_t);
+extern uint32_t nl_dect_ari_get_fps(const struct nl_dect_ari *);
+extern void nl_dect_ari_set_emc(struct nl_dect_ari *, uint16_t);
+extern uint16_t nl_dect_ari_get_emc(const struct nl_dect_ari *);
+extern void nl_dect_ari_set_eic(struct nl_dect_ari *, uint16_t);
+extern uint16_t nl_dect_ari_get_eic(const struct nl_dect_ari *);
+extern void nl_dect_ari_set_poc(struct nl_dect_ari *, uint16_t);
+extern uint16_t nl_dect_ari_get_poc(const struct nl_dect_ari *);
+extern void nl_dect_ari_set_gop(struct nl_dect_ari *, uint32_t);
+extern uint32_t nl_dect_ari_get_gop(const struct nl_dect_ari *);
+extern void nl_dect_ari_set_fil(struct nl_dect_ari *, uint16_t);
+extern uint16_t nl_dect_ari_get_fil(const struct nl_dect_ari *);
+
+#endif /* NETLINK_DECT_ARI_H */
diff --git a/include/netlink/dect/cell.h b/include/netlink/dect/cell.h
new file mode 100644
index 0000000..e1b0f30
--- /dev/null
+++ b/include/netlink/dect/cell.h
@@ -0,0 +1,61 @@
+#ifndef NETLINK_DECT_CELL_H
+#define NETLINK_DECT_CELL_H
+
+#include <stdbool.h>
+
+struct nl_dect_cell;
+struct nl_dect_ari;
+
+extern struct nl_object_ops nl_dect_cell_obj_ops;
+
+extern struct nl_dect_cell * nl_dect_cell_alloc(void);
+extern void nl_dect_cell_get(struct nl_dect_cell *);
+extern void nl_dect_cell_put(struct nl_dect_cell *);
+
+extern void nl_dect_cell_set_index(struct nl_dect_cell *, int);
+extern bool nl_dect_cell_test_index(const struct nl_dect_cell *);
+extern int nl_dect_cell_get_index(const struct nl_dect_cell *);
+
+extern void nl_dect_cell_set_name(struct nl_dect_cell *, const char *);
+extern bool nl_dect_cell_test_name(const struct nl_dect_cell *);
+extern const char * nl_dect_cell_get_name(const struct nl_dect_cell *);
+
+extern void nl_dect_cell_set_flags(struct nl_dect_cell *, uint32_t);
+extern bool nl_dect_cell_test_flags(const struct nl_dect_cell *);
+extern uint32_t nl_dect_cell_get_flags(const struct nl_dect_cell *);
+
+extern void nl_dect_cell_set_transceiver(struct nl_dect_cell *,
+ unsigned int,
+ const char *);
+extern bool nl_dect_cell_test_transceiver(const struct nl_dect_cell *);
+extern const char * nl_dect_cell_get_transceiver(const struct nl_dect_cell *,
+ unsigned int);
+
+extern void nl_dect_cell_set_link(struct nl_dect_cell *, int);
+extern bool nl_dect_cell_test_link(const struct nl_dect_cell *);
+extern int nl_dect_cell_get_link(const struct nl_dect_cell *);
+
+extern int nl_dect_cell_alloc_cache(struct nl_sock *,
+ struct nl_cache **);
+
+extern int nl_dect_cell_build_msg(struct nl_msg *,
+ struct nl_dect_cell *);
+
+extern int nl_dect_cell_build_add_request(struct nl_dect_cell *,
+ int, struct nl_msg **);
+extern int nl_dect_cell_add(struct nl_sock *,
+ struct nl_dect_cell *, int);
+
+extern int nl_dect_cell_build_del_request(struct nl_dect_cell *,
+ int, struct nl_msg **);
+extern int nl_dect_cell_delete(struct nl_sock *,
+ struct nl_dect_cell *, int);
+
+extern char * nl_dect_cell_i2name(struct nl_cache *, int, char *,
+ size_t);
+extern int nl_dect_cell_name2i(struct nl_cache *, const char *);
+
+extern char * nl_dect_cell_flags2str(uint32_t, char *, size_t);
+extern uint32_t nl_dect_cell_str2flags(const char *);
+
+#endif /* NETLINK_DECT_CELL_H */
diff --git a/include/netlink/dect/cluster.h b/include/netlink/dect/cluster.h
new file mode 100644
index 0000000..43f79e1
--- /dev/null
+++ b/include/netlink/dect/cluster.h
@@ -0,0 +1,61 @@
+#ifndef NETLINK_DECT_CLUSTER_H
+#define NETLINK_DECT_CLUSTER_H
+
+#include <stdbool.h>
+
+struct nl_dect_cluster;
+struct nl_dect_ari;
+
+extern struct nl_object_ops nl_dect_cluster_obj_ops;
+
+extern struct nl_dect_cluster * nl_dect_cluster_alloc(void);
+extern void nl_dect_cluster_get(struct nl_dect_cluster *);
+extern void nl_dect_cluster_put(struct nl_dect_cluster *);
+
+extern unsigned int nl_dect_cluster_get_index(const struct nl_dect_cluster *);
+
+extern void nl_dect_cluster_set_name(struct nl_dect_cluster *,
+ const char *);
+extern bool nl_dect_cluster_test_name(const struct nl_dect_cluster *);
+extern const char * nl_dect_cluster_get_name(const struct nl_dect_cluster *);
+
+extern void nl_dect_cluster_set_mode(struct nl_dect_cluster *,
+ uint8_t mode);
+extern bool nl_dect_cluster_test_mode(const struct nl_dect_cluster *);
+extern uint8_t nl_dect_cluster_get_mode(const struct nl_dect_cluster *);
+
+extern void nl_dect_cluster_set_pari(struct nl_dect_cluster *,
+ const struct nl_dect_ari *);
+extern bool nl_dect_cluster_test_pari(const struct nl_dect_cluster *);
+extern const struct nl_dect_ari *nl_dect_cluster_get_pari(const struct nl_dect_cluster *);
+
+extern char * nl_dect_cluster_mode2str(enum dect_cluster_modes,
+ char *, size_t);
+extern enum dect_cluster_modes nl_dect_cluster_str2mode(const char *);
+
+extern int nl_dect_cluster_alloc_cache(struct nl_sock *,
+ struct nl_cache **);
+
+extern int nl_dect_cluster_build_msg(struct nl_msg *,
+ struct nl_dect_cluster *);
+
+extern int nl_dect_cluster_build_add_request(struct nl_dect_cluster *,
+ int, struct nl_msg **);
+extern int nl_dect_cluster_add(struct nl_sock *,
+ struct nl_dect_cluster *, int);
+
+extern int nl_dect_cluster_build_del_request(struct nl_dect_cluster *,
+ int, struct nl_msg **);
+extern int nl_dect_cluster_delete(struct nl_sock *,
+ struct nl_dect_cluster *, int);
+
+extern int nl_dect_cluster_build_query_request(struct nl_dect_cluster *,
+ int, struct nl_msg **);
+extern int nl_dect_cluster_query(struct nl_sock *,
+ struct nl_dect_cluster *, int);
+
+extern char * nl_dect_cluster_i2name(struct nl_cache *, int, char *,
+ size_t);
+extern int nl_dect_cluster_name2i(struct nl_cache *, const char *);
+
+#endif /* NETLINK_DECT_CLUSTER_H */
diff --git a/include/netlink/dect/dect.h b/include/netlink/dect/dect.h
new file mode 100644
index 0000000..2a28864
--- /dev/null
+++ b/include/netlink/dect/dect.h
@@ -0,0 +1,6 @@
+#ifndef _NETLINK_DECT_H
+#define _NETLINK_DECT_H
+
+extern double nl_dect_rssi_to_dbm(uint8_t);
+
+#endif /* _NETLINK_DECT_H */
diff --git a/include/netlink/dect/llme.h b/include/netlink/dect/llme.h
new file mode 100644
index 0000000..85cf9cc
--- /dev/null
+++ b/include/netlink/dect/llme.h
@@ -0,0 +1,94 @@
+#ifndef NETLINK_DECT_LLME_H
+#define NETLINK_DECT_LLME_H
+
+#include <stdbool.h>
+
+struct nl_dect_llme_msg;
+struct nl_dect_ari;
+
+extern struct nl_dect_llme_msg *nl_dect_llme_msg_alloc(void);
+extern void nl_dect_llme_msg_get(struct nl_dect_llme_msg *);
+extern void nl_dect_llme_msg_put(struct nl_dect_llme_msg *);
+
+extern int nl_dect_llme_build_request(struct nl_dect_llme_msg *,
+ struct nl_msg **);
+extern int nl_dect_llme_request(struct nl_sock *,
+ struct nl_dect_llme_msg *);
+
+extern int nl_dect_llme_build_response(struct nl_dect_llme_msg *,
+ struct nl_msg **);
+extern int nl_dect_llme_respond(struct nl_sock *, struct nl_dect_llme_msg *);
+
+extern void nl_dect_llme_msg_set_index(struct nl_dect_llme_msg *, int);
+extern void nl_dect_llme_msg_set_type(struct nl_dect_llme_msg *,
+ enum dect_llme_msg_types);
+extern enum dect_llme_msg_types nl_dect_llme_msg_get_type(const struct nl_dect_llme_msg *);
+extern void nl_dect_llme_msg_set_op(struct nl_dect_llme_msg *,
+ enum dect_llme_ops);
+extern enum dect_llme_ops nl_dect_llme_msg_get_op(const struct nl_dect_llme_msg *);
+
+extern char * nl_dect_llme_msgtype2str(enum dect_llme_msg_types,
+ char *, size_t);
+extern enum dect_llme_msg_types nl_dect_llme_str2msgtype(const char *);
+
+extern char * nl_dect_llme_op2str(enum dect_llme_ops,
+ char *, size_t);
+extern enum dect_llme_ops nl_dect_llme_str2op(const char *);
+
+
+extern void nl_dect_llme_mac_info_set_pari(struct nl_dect_llme_msg *lmsg,
+ const struct nl_dect_ari *);
+extern bool nl_dect_llme_mac_info_test_pari(const struct nl_dect_llme_msg *);
+extern const struct nl_dect_ari *nl_dect_llme_mac_info_get_pari(const struct nl_dect_llme_msg *);
+
+extern void nl_dect_llme_mac_info_set_rpn(struct nl_dect_llme_msg *,
+ uint8_t);
+extern uint8_t nl_dect_llme_mac_info_get_rpn(const struct nl_dect_llme_msg *);
+
+extern void nl_dect_llme_mac_info_set_rssi(struct nl_dect_llme_msg *,
+ uint8_t);
+extern uint8_t nl_dect_llme_mac_info_get_rssi(const struct nl_dect_llme_msg *);
+
+extern void nl_dect_llme_mac_info_set_fpc(struct nl_dect_llme_msg *,
+ uint32_t);
+extern uint32_t nl_dect_llme_mac_info_get_fpc(const struct nl_dect_llme_msg *);
+
+extern void nl_dect_llme_mac_info_set_hlc(struct nl_dect_llme_msg *,
+ uint16_t);
+extern uint16_t nl_dect_llme_mac_info_get_hlc(const struct nl_dect_llme_msg *);
+
+extern void nl_dect_llme_mac_info_set_efpc(struct nl_dect_llme_msg *,
+ uint16_t);
+extern uint16_t nl_dect_llme_mac_info_get_efpc(const struct nl_dect_llme_msg *);
+
+extern void nl_dect_llme_mac_info_set_ehlc(struct nl_dect_llme_msg *,
+ uint32_t);
+extern uint32_t nl_dect_llme_mac_info_get_ehlc(const struct nl_dect_llme_msg *);
+
+extern void nl_dect_llme_mac_info_set_efpc2(struct nl_dect_llme_msg *,
+ uint16_t);
+extern uint16_t nl_dect_llme_mac_info_get_efpc2(const struct nl_dect_llme_msg *);
+
+extern void nl_dect_llme_mac_info_set_ehlc2(struct nl_dect_llme_msg *,
+ uint32_t);
+extern uint32_t nl_dect_llme_mac_info_get_ehlc2(const struct nl_dect_llme_msg *);
+
+extern char * nl_dect_llme_fpc2str(uint32_t, char *, size_t);
+extern uint32_t nl_dect_llme_str2fpc(const char *);
+
+extern char * nl_dect_llme_hlc2str(uint16_t, char *, size_t);
+extern uint16_t nl_dect_llme_str2hlc(const char *);
+
+extern char * nl_dect_llme_efpc2str(uint16_t, char *, size_t);
+extern uint16_t nl_dect_llme_str2efpc(const char *);
+
+extern char * nl_dect_llme_ehlc2str(uint32_t, char *, size_t);
+extern uint32_t nl_dect_llme_str2ehlc(const char *);
+
+extern char * nl_dect_llme_efpc22str(uint16_t, char *, size_t);
+extern uint16_t nl_dect_llme_str22efpc(const char *);
+
+extern char * nl_dect_llme_ehlc22str(uint32_t, char *, size_t);
+extern uint32_t nl_dect_llme_str22ehlc(const char *);
+
+#endif /* NETLINK_DECT_LLME_H */
diff --git a/include/netlink/dect/transceiver.h b/include/netlink/dect/transceiver.h
new file mode 100644
index 0000000..28dff52
--- /dev/null
+++ b/include/netlink/dect/transceiver.h
@@ -0,0 +1,57 @@
+#ifndef NETLINK_DECT_TRANSCEIVER_H
+#define NETLINK_DECT_TRANSCEIVER_H
+
+#include <stdbool.h>
+
+struct nl_dect_transceiver;
+
+extern struct nl_object_ops nl_dect_transceiver_obj_ops;
+
+extern struct nl_dect_transceiver *nl_dect_transceiver_alloc(void);
+extern void nl_dect_transceiver_get(struct nl_dect_transceiver *);
+extern void nl_dect_transceiver_put(struct nl_dect_transceiver *);
+
+extern void nl_dect_transceiver_set_name(struct nl_dect_transceiver *,
+ const char *);
+extern bool nl_dect_transceiver_test_name(const struct nl_dect_transceiver *);
+extern const char * nl_dect_transceiver_get_name(const struct nl_dect_transceiver *);
+
+extern void nl_dect_transceiver_set_type(struct nl_dect_transceiver *,
+ const char *);
+extern bool nl_dect_transceiver_test_type(const struct nl_dect_transceiver *);
+extern const char * nl_dect_transceiver_get_type(const struct nl_dect_transceiver *);
+
+extern void nl_dect_transceiver_set_features(struct nl_dect_transceiver *,
+ uint32_t);
+extern void nl_dect_transceiver_set_index(struct nl_dect_transceiver *,
+ int);
+extern void nl_dect_transceiver_set_link(struct nl_dect_transceiver *,
+ uint8_t);
+
+extern void nl_dect_transceiver_set_band(struct nl_dect_transceiver *,
+ uint8_t);
+extern bool nl_dect_transceiver_test_band(const struct nl_dect_transceiver *);
+extern uint8_t nl_dect_transceiver_get_band(const struct nl_dect_transceiver *);
+
+extern int nl_dect_transceiver_build_msg(struct nl_msg *,
+ struct nl_dect_transceiver *);
+extern int nl_dect_transceiver_build_change_request(struct nl_dect_transceiver *,
+ int, struct nl_msg **);
+extern int nl_dect_transceiver_change(struct nl_sock *,
+ struct nl_dect_transceiver *, int flags);
+
+extern const char * nl_dect_transceiver_features2str(uint32_t, char *, size_t);
+
+extern const char * nl_dect_slot_state2str(uint8_t, char *, size_t);
+extern uint8_t nl_dect_slot_str2state(const char *);
+
+extern const char * nl_dect_slot_flags2str(uint32_t, char *, size_t);
+extern uint32_t nl_dect_slot_str2flags(const char *);
+
+extern const char * nl_dect_slot_packet2str(uint8_t, char *, size_t);
+extern uint8_t nl_dect_slot_str2packet(const char *);
+
+extern int nl_dect_transceiver_alloc_cache(struct nl_sock *,
+ struct nl_cache **);
+
+#endif /* NETLINK_DECT_TRANSCEIVER_H */
diff --git a/include/netlink/netlink.h b/include/netlink/netlink.h
index 1d74ba1..c65c303 100644
--- a/include/netlink/netlink.h
+++ b/include/netlink/netlink.h
@@ -26,6 +26,7 @@
#include <linux/rtnetlink.h>
#include <linux/genetlink.h>
#include <linux/netfilter/nfnetlink.h>
+#include <linux/dect_netlink.h>
#include <netlink/version.h>
#include <netlink/errno.h>
#include <netlink/types.h>
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 677a89c..438b3eb 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -13,7 +13,7 @@ AM_LDFLAGS = \
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
lib_LTLIBRARIES = \
- libnl-3.la libnl-genl-3.la libnl-route-3.la libnl-nf-3.la
+ libnl-3.la libnl-genl-3.la libnl-route-3.la libnl-nf-3.la libnl-dect-3.la
libnl_3_la_SOURCES = \
addr.c attr.c cache.c cache_mngr.c cache_mngt.c data.c \
@@ -32,6 +32,12 @@ libnl_nf_3_la_SOURCES = \
netfilter/queue_msg.c netfilter/queue_msg_obj.c netfilter/queue_obj.c \
netfilter/exp.c netfilter/exp_obj.c
+libnl_dect_3_la_LIBADD = libnl-3.la
+libnl_dect_3_la_SOURCES = \
+ dect/ari.c dect/cell.c dect/cell_obj.c \
+ dect/cluster.c dect/cluster_obj.c dect/dect.c dect/llme.c \
+ dect/transceiver.c dect/transceiver_obj.c
+
CLEANFILES = \
route/pktloc_grammar.c route/pktloc_grammar.h \
route/pktloc_syntax.c route/pktloc_syntax.h \
diff --git a/lib/dect/ari.c b/lib/dect/ari.c
new file mode 100644
index 0000000..d17d4f8
--- /dev/null
+++ b/lib/dect/ari.c
@@ -0,0 +1,245 @@
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/dect/ari.h>
+#include <linux/dect_netlink.h>
+
+#define ARI_ATTR_CLASS 0x01
+#define ARI_ATTR_FPN 0x02
+#define ARI_ATTR_FPS 0x03
+#define ARI_ATTR_EMC 0x04
+#define ARI_ATTR_EIC 0x05
+#define ARI_ATTR_POC 0x06
+#define ARI_ATTR_GOP 0x07
+#define ARI_ATTR_FIL 0x08
+
+static struct nla_policy nl_dect_ari_policy[DECTA_ARI_MAX + 1] = {
+ [DECTA_ARI_CLASS] = { .type = NLA_U8 },
+ [DECTA_ARI_FPN] = { .type = NLA_U32 },
+ [DECTA_ARI_FPS] = { .type = NLA_U32 },
+ [DECTA_ARI_EMC] = { .type = NLA_U16 },
+ [DECTA_ARI_EIC] = { .type = NLA_U16 },
+ [DECTA_ARI_POC] = { .type = NLA_U16 },
+ [DECTA_ARI_GOP] = { .type = NLA_U32 },
+ [DECTA_ARI_FIL] = { .type = NLA_U32 },
+};
+
+int nl_dect_parse_ari(struct nl_dect_ari *ari, struct nlattr *nla)
+{
+ struct nlattr *tb[DECTA_ARI_MAX + 1];
+ int err;
+
+ err = nla_parse_nested(tb, DECTA_ARI_MAX, nla, nl_dect_ari_policy);
+ if (err < 0)
+ return err;
+
+ if (tb[DECTA_ARI_CLASS] == NULL)
+ return -NLE_INVAL;
+
+ memset(ari, 0, sizeof(ari));
+ ari->ari_class = nla_get_u8(tb[DECTA_ARI_CLASS]);
+ if (tb[DECTA_ARI_FPN] != NULL)
+ ari->ari_fpn = nla_get_u32(tb[DECTA_ARI_FPN]);
+
+ switch (ari->ari_class) {
+ case DECT_ARC_A:
+ if (tb[DECTA_ARI_EMC] != NULL)
+ ari->ari_u.emc = nla_get_u16(tb[DECTA_ARI_EMC]);
+ break;
+ case DECT_ARC_B:
+ if (tb[DECTA_ARI_EIC] != NULL)
+ ari->ari_u.eic = nla_get_u16(tb[DECTA_ARI_EIC]);
+ if (tb[DECTA_ARI_FPS] != NULL)
+ ari->ari_fps = nla_get_u32(tb[DECTA_ARI_FPS]);
+ break;
+ case DECT_ARC_C:
+ if (tb[DECTA_ARI_POC] != NULL)
+ ari->ari_u.poc = nla_get_u16(tb[DECTA_ARI_POC]);
+ if (tb[DECTA_ARI_FPS] != NULL)
+ ari->ari_fps = nla_get_u32(tb[DECTA_ARI_FPS]);
+ break;
+ case DECT_ARC_D:
+ if (tb[DECTA_ARI_GOP] != NULL)
+ ari->ari_u.gop = nla_get_u32(tb[DECTA_ARI_GOP]);
+ break;
+ case DECT_ARC_E:
+ if (tb[DECTA_ARI_FIL] != NULL)
+ ari->ari_u.fil = nla_get_u16(tb[DECTA_ARI_FIL]);
+ break;
+ default:
+ return -NLE_INVAL;
+ }
+
+ return 0;
+}
+
+int nl_dect_fill_ari(struct nl_msg *msg, const struct nl_dect_ari *ari, int attr)
+{
+ struct nlattr *nla;
+
+ nla = nla_nest_start(msg, attr);
+ if (nla == NULL)
+ goto nla_put_failure;
+
+ NLA_PUT_U8(msg, DECTA_ARI_CLASS, ari->ari_class);
+ NLA_PUT_U32(msg, DECTA_ARI_FPN, ari->ari_fpn);
+
+ switch (ari->ari_class) {
+ case DECT_ARC_A:
+ NLA_PUT_U16(msg, DECTA_ARI_EMC, ari->ari_u.emc);
+ break;
+ case DECT_ARC_B:
+ NLA_PUT_U16(msg, DECTA_ARI_EIC, ari->ari_u.eic);
+ NLA_PUT_U32(msg, DECTA_ARI_FPS, ari->ari_fps);
+ break;
+ case DECT_ARC_C:
+ NLA_PUT_U16(msg, DECTA_ARI_POC, ari->ari_u.poc);
+ NLA_PUT_U32(msg, DECTA_ARI_FPS, ari->ari_fps);
+ break;
+ case DECT_ARC_D:
+ NLA_PUT_U32(msg, DECTA_ARI_GOP, ari->ari_u.gop);
+ break;
+ case DECT_ARC_E:
+ NLA_PUT_U16(msg, DECTA_ARI_FIL, ari->ari_u.fil);
+ break;
+ }
+ nla_nest_end(msg, nla);
+ return 0;
+
+nla_put_failure:
+ return -NLE_MSGSIZE;
+}
+
+void nl_dect_dump_ari(const struct nl_dect_ari *ari, struct nl_dump_params *p)
+{
+ nl_dump(p, "class %c ", 'A' + ari->ari_class);
+
+ switch (ari->ari_class) {
+ case DECT_ARC_A:
+ nl_dump(p, "(residential) EMC: %.4x FPN: %.5x",
+ ari->ari_u.emc, ari->ari_fpn);
+ break;
+ case DECT_ARC_B:
+ nl_dump(p, "(private multiple cell) EIC: %.4x FPN: %.2x FPS: %x",
+ ari->ari_u.eic, ari->ari_fpn, ari->ari_fps);
+ break;
+ case DECT_ARC_C:
+ nl_dump(p, "(public) POC: %.4x FPN: %.2x FPS: %x",
+ ari->ari_u.poc, ari->ari_fpn, ari->ari_fps);
+ break;
+ case DECT_ARC_D:
+ nl_dump(p, "(public GSM) GOP: %.5x FPN: %.2x",
+ ari->ari_u.gop, ari->ari_fpn);
+ break;
+ case DECT_ARC_E:
+ nl_dump(p, "(PP to PP) FIL: %.4x FPN: %.3x",
+ ari->ari_u.fil, ari->ari_fpn);
+ }
+
+}
+
+static struct trans_tbl ari_classes[] = {
+ __ADD(DECT_ARC_A, a)
+ __ADD(DECT_ARC_B, b)
+ __ADD(DECT_ARC_C, c)
+ __ADD(DECT_ARC_D, d)
+ __ADD(DECT_ARC_E, e)
+};
+
+const char *nl_dect_ari_class2str(enum dect_ari_classes class, char *buf, size_t len)
+{
+ return __type2str(class, buf, len, ari_classes, ARRAY_SIZE(ari_classes));
+}
+
+enum dect_ari_classes nl_dect_ari_str2class(const char *str)
+{
+ return __str2type(str, ari_classes, ARRAY_SIZE(ari_classes));
+}
+
+void nl_dect_ari_set_class(struct nl_dect_ari *ari, enum dect_ari_classes class)
+{
+ ari->ari_flags |= ARI_ATTR_CLASS;
+ ari->ari_class = class;
+}
+
+enum dect_ari_classes nl_dect_ari_get_class(const struct nl_dect_ari *ari)
+{
+ return ari->ari_class;
+}
+
+void nl_dect_ari_set_fpn(struct nl_dect_ari *ari, uint32_t fpn)
+{
+ ari->ari_flags |= ARI_ATTR_FPN;
+ ari->ari_fpn = fpn;
+}
+
+uint32_t nl_dect_ari_get_fpn(const struct nl_dect_ari *ari)
+{
+ return ari->ari_fpn;
+}
+
+void nl_dect_ari_set_fps(struct nl_dect_ari *ari, uint32_t fps)
+{
+ ari->ari_flags |= ARI_ATTR_FPS;
+ ari->ari_fps = fps;
+}
+
+uint32_t nl_dect_ari_get_fps(const struct nl_dect_ari *ari)
+{
+ return ari->ari_fps;
+}
+
+void nl_dect_ari_set_emc(struct nl_dect_ari *ari, uint16_t emc)
+{
+ ari->ari_flags |= ARI_ATTR_EMC;
+ ari->ari_u.emc = emc;
+}
+
+uint16_t nl_dect_ari_get_emc(const struct nl_dect_ari *ari)
+{
+ return ari->ari_u.emc;
+}
+
+void nl_dect_ari_set_eic(struct nl_dect_ari *ari, uint16_t eic)
+{
+ ari->ari_flags |= ARI_ATTR_EIC;
+ ari->ari_u.eic = eic;
+}
+
+uint16_t nl_dect_ari_get_eic(const struct nl_dect_ari *ari)
+{
+ return ari->ari_u.eic;
+}
+
+void nl_dect_ari_set_poc(struct nl_dect_ari *ari, uint16_t poc)
+{
+ ari->ari_flags |= ARI_ATTR_POC;
+ ari->ari_u.poc = poc;
+}
+
+uint16_t nl_dect_ari_get_poc(const struct nl_dect_ari *ari)
+{
+ return ari->ari_u.poc;
+}
+
+void nl_dect_ari_set_gop(struct nl_dect_ari *ari, uint32_t gop)
+{
+ ari->ari_flags |= ARI_ATTR_GOP;
+ ari->ari_u.gop = gop;
+}
+
+uint32_t nl_dect_ari_get_gop(const struct nl_dect_ari *ari)
+{
+ return ari->ari_u.gop;
+}
+
+void nl_dect_ari_set_fil(struct nl_dect_ari *ari, uint16_t fil)
+{
+ ari->ari_flags |= ARI_ATTR_FIL;
+ ari->ari_u.fil = fil;
+}
+
+uint16_t nl_dect_ari_get_fil(const struct nl_dect_ari *ari)
+{
+ return ari->ari_u.fil;
+}
diff --git a/lib/dect/cell.c b/lib/dect/cell.c
new file mode 100644
index 0000000..f7081d9
--- /dev/null
+++ b/lib/dect/cell.c
@@ -0,0 +1,267 @@
+/*
+ * lib/nl_dect/cell.c DECT Cell objects
+ *
+ * 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 version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
+ */
+
+/**
+ * @ingroup nl_dect
+ * @defgroup nl_dect Transceivers
+ * @brief
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/dect/cell.h>
+#include <netlink/dect/ari.h>
+#include <linux/dect_netlink.h>
+
+/** @cond SKIP */
+static struct nl_cache_ops nl_dect_cell_ops;
+/** @endcond */
+
+static struct nla_policy cell_policy[DECTA_CELL_MAX + 1] = {
+ [DECTA_CELL_NAME] = { .type = NLA_STRING, .maxlen = 16 },
+ [DECTA_CELL_FLAGS] = { .type = NLA_U32 },
+ [DECTA_CELL_TRANSCEIVERS] = { .type = NLA_NESTED },
+ [DECTA_CELL_CLUSTER] = { .type = NLA_U8 },
+};
+
+static int cell_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+ struct nlmsghdr *n, struct nl_parser_param *pp)
+{
+ struct dectmsg *dm = nlmsg_data(n);
+ struct nl_dect_cell *cell;
+ struct nlattr *tb[DECTA_CELL_MAX + 1], *nla;
+ int rem, err;
+
+ err = nlmsg_parse(n, sizeof(*dm), tb, DECTA_CELL_MAX, cell_policy);
+ if (err < 0)
+ return err;
+
+ cell = nl_dect_cell_alloc();
+ if (cell == NULL) {
+ err = -NLE_NOMEM;
+ goto errout;
+ }
+
+ cell->ce_msgtype = n->nlmsg_type;
+ cell->c_index = dm->dm_index;
+
+ if (tb[DECTA_CELL_NAME] != NULL)
+ nl_dect_cell_set_name(cell, nla_data(tb[DECTA_CELL_NAME]));
+ if (tb[DECTA_CELL_FLAGS] != NULL)
+ nl_dect_cell_set_flags(cell, nla_get_u32(tb[DECTA_CELL_FLAGS]));
+ if (tb[DECTA_CELL_TRANSCEIVERS] != NULL) {
+ unsigned int i = 0;
+ nla_for_each_nested(nla, tb[DECTA_CELL_TRANSCEIVERS], rem) {
+ char *id = nla_strdup(nla);
+ if (id == NULL) {
+ err = -NLE_NOMEM;
+ goto errout;
+ }
+ nl_dect_cell_set_transceiver(cell, i++, id);
+ }
+ }
+ if (tb[DECTA_CELL_CLUSTER] != NULL)
+ nl_dect_cell_set_link(cell, nla_get_u8(tb[DECTA_CELL_CLUSTER]));
+
+ err = pp->pp_cb((struct nl_object *)cell, pp);
+errout:
+ nl_dect_cell_put(cell);
+ return err;
+}
+
+static int cell_request_update(struct nl_cache *c, struct nl_sock *h)
+{
+ struct dectmsg dm;
+
+ memset(&dm, 0, sizeof(dm));
+ return nl_send_simple(h, DECT_GET_CELL, NLM_F_DUMP, &dm, sizeof(dm));
+}
+
+/**
+ * @name Cache Management
+ * @{
+ */
+int nl_dect_cell_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
+{
+ struct nl_cache *cache;
+ int err;
+
+ cache = nl_cache_alloc(&nl_dect_cell_ops);
+ if (cache == NULL)
+ return -NLE_NOMEM;
+
+ if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
+ free(cache);
+ return err;
+ }
+
+ *result = cache;
+ return 0;
+}
+
+struct nl_dect_cell *nl_dect_cell_get_by_name(struct nl_cache *cache, const char *name)
+{
+ struct nl_dect_cell *cell;
+
+ nl_list_for_each_entry(cell, &cache->c_items, ce_list) {
+ if (!strcmp(cell->c_name, name)) {
+ nl_object_get((struct nl_object *)cell);
+ return cell;
+ }
+ }
+ return NULL;
+}
+
+struct nl_dect_cell *nl_dect_cell_get_by_index(struct nl_cache *cache, int index)
+{
+ struct nl_dect_cell *cell;
+
+ nl_list_for_each_entry(cell, &cache->c_items, ce_list) {
+ if (cell->c_index == index) {
+ nl_object_get((struct nl_object *)cell);
+ return cell;
+ }
+ }
+ return NULL;
+}
+
+/** @} */
+
+/**
+ * @name Device creation
+ * @{
+ */
+
+static int build_cell_msg(struct nl_dect_cell *tmpl, int cmd, int flags,
+ struct nl_msg **result)
+{
+ struct nl_msg *msg;
+ int err;
+
+ msg = nlmsg_alloc_simple(cmd, flags);
+ if (msg == NULL)
+ return -NLE_NOMEM;
+
+ err = nl_dect_cell_build_msg(msg, tmpl);
+ if (err < 0) {
+ nlmsg_free(msg);
+ return err;
+ }
+
+ *result = msg;
+ return 0;
+}
+
+int nl_dect_cell_build_add_request(struct nl_dect_cell *tmpl, int flags,
+ struct nl_msg **result)
+{
+ return build_cell_msg(tmpl, DECT_NEW_CELL, NLM_F_CREATE | flags,
+ result);
+}
+
+int nl_dect_cell_add(struct nl_sock *sk, struct nl_dect_cell *cell, int flags)
+{
+ struct nl_msg *msg;
+ int err;
+
+ err = nl_dect_cell_build_add_request(cell, flags, &msg);
+ if (err < 0)
+ return err;
+
+ err = nl_send_auto_complete(sk, msg);
+ nlmsg_free(msg);
+ if (err < 0)
+ return err;
+
+ return wait_for_ack(sk);
+}
+
+int nl_dect_cell_build_del_request(struct nl_dect_cell *tmpl, int flags,
+ struct nl_msg **result)
+{
+ return build_cell_msg(tmpl, DECT_DEL_CELL, flags, result);
+}
+
+int nl_dect_cell_delete(struct nl_sock *sk, struct nl_dect_cell *tmpl, int flags)
+{
+ struct nl_msg *msg;
+ int err;
+
+ err = nl_dect_cell_build_del_request(tmpl, flags, &msg);
+ if (err < 0)
+ return err;
+
+ err = nl_send_auto_complete(sk, msg);
+ nlmsg_free(msg);
+ if (err < 0)
+ return err;
+
+ return wait_for_ack(sk);
+}
+
+/** @} */
+
+/**
+ * @name Name <-> Index Translations
+ * @{
+ */
+
+char *nl_dect_cell_i2name(struct nl_cache *cache, int index, char *buf, size_t len)
+{
+ struct nl_dect_cell *cell = nl_dect_cell_get_by_index(cache, index);
+
+ if (cell != NULL) {
+ strncpy(buf, cell->c_name, len - 1);
+ buf[len - 1] = 0;
+ return buf;
+ }
+ return NULL;
+}
+
+int nl_dect_cell_name2i(struct nl_cache *cache, const char *name)
+{
+ struct nl_dect_cell *cell = nl_dect_cell_get_by_name(cache, name);
+
+ if (cell != NULL)
+ return cell->c_index;
+ return 0;
+}
+
+/** @} */
+
+static struct nl_cache_ops nl_dect_cell_ops = {
+ .co_name = "nl_dect/cell",
+ .co_hdrsize = 0,
+ .co_msgtypes = {
+ { DECT_NEW_CELL, NL_ACT_NEW, "new" },
+ { DECT_DEL_CELL, NL_ACT_NEW, "del" },
+ { DECT_GET_CELL, NL_ACT_GET, "get" },
+ END_OF_MSGTYPES_LIST
+ },
+ .co_protocol = NETLINK_DECT,
+ .co_request_update = cell_request_update,
+ .co_msg_parser = cell_msg_parser,
+ .co_obj_ops = &nl_dect_cell_obj_ops,
+};
+
+static void __init cell_init(void)
+{
+ nl_cache_mngt_register(&nl_dect_cell_ops);
+}
+
+static void __exit cell_exit(void)
+{
+ nl_cache_mngt_unregister(&nl_dect_cell_ops);
+}
+
+/** @} */
diff --git a/lib/dect/cell_obj.c b/lib/dect/cell_obj.c
new file mode 100644
index 0000000..603fef0
--- /dev/null
+++ b/lib/dect/cell_obj.c
@@ -0,0 +1,234 @@
+/*
+ * lib/dect/cell.c DECT Cell objects
+ *
+ * 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 version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/utils.h>
+#include <netlink/data.h>
+#include <netlink/dect/cluster.h>
+#include <netlink/dect/cell.h>
+#include <netlink/dect/ari.h>
+#include <linux/dect_netlink.h>
+
+/** @cond SKIP */
+#define CELL_ATTR_INDEX 0x0001
+#define CELL_ATTR_NAME 0x0002
+#define CELL_ATTR_FLAGS 0x0004
+#define CELL_ATTR_TRANSCEIVER 0x0008
+#define CELL_ATTR_LINK 0x0010
+/** @endcond */
+
+static void cell_free_data(struct nl_object *obj)
+{
+ struct nl_dect_cell *cell = nl_object_priv(obj);
+ unsigned int i;
+
+ if (cell == NULL)
+ return;
+ for (i = 0; i < 16; i++)
+ free(cell->c_transceiver[i]);
+}
+
+static void cell_dump(struct nl_object *obj, struct nl_dump_params *p)
+{
+ struct nl_dect_cell *cell = nl_object_priv(obj);
+ struct nl_cache *cluster_cache;
+ unsigned int i;
+ char buf[64];
+
+ if (cell->ce_mask & CELL_ATTR_NAME)
+ nl_dump(p, "%u: DECT Cell %s", cell->c_index, cell->c_name);
+
+ if (cell->ce_mask & CELL_ATTR_LINK) {
+ cluster_cache = nl_cache_mngt_require("nl_dect/cluster");
+ if (cluster_cache != NULL) {
+ nl_dect_cluster_i2name(cluster_cache, cell->c_link, buf,
+ sizeof(buf));
+ nl_dump(p, "@%s", buf);
+ } else
+ nl_dump(p, "@%u", cell->c_link);
+ }
+ nl_dump(p, ":");
+
+ if (cell->ce_mask & CELL_ATTR_FLAGS) {
+ nl_dect_cell_flags2str(cell->c_flags, buf, sizeof(buf));
+ nl_dump(p, " <%s>", buf);
+ }
+ nl_dump(p, "\n");
+
+ if (cell->ce_mask & CELL_ATTR_TRANSCEIVER) {
+ for (i = 0; i < 16 && cell->c_transceiver[i] != NULL; i++)
+ nl_dump(p, "\tTransceiver: %s\n",
+ cell->c_transceiver[i]);
+ }
+
+}
+
+/**
+ * @name Allocation/Freeing
+ * @{
+ */
+
+struct nl_dect_cell *nl_dect_cell_alloc(void)
+{
+ return (struct nl_dect_cell *)nl_object_alloc(&nl_dect_cell_obj_ops);
+}
+
+void nl_dect_cell_get(struct nl_dect_cell *cell)
+{
+ nl_object_get((struct nl_object *)cell);
+}
+
+void nl_dect_cell_put(struct nl_dect_cell *cell)
+{
+ nl_object_put((struct nl_object *)cell);
+}
+
+/** @} */
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+void nl_dect_cell_set_index(struct nl_dect_cell *cell, int index)
+{
+ cell->c_index = index;
+ cell->ce_mask |= CELL_ATTR_INDEX;
+}
+
+bool nl_dect_cell_test_index(const struct nl_dect_cell *cell)
+{
+ return !!(cell->ce_mask & CELL_ATTR_INDEX);
+}
+
+int nl_dect_cell_get_index(const struct nl_dect_cell *cell)
+{
+ return cell->c_index;
+}
+
+void nl_dect_cell_set_name(struct nl_dect_cell *cell, const char *name)
+{
+ cell->c_name = strdup(name);
+ cell->ce_mask |= CELL_ATTR_NAME;
+}
+
+bool nl_dect_cell_test_name(const struct nl_dect_cell *cell)
+{
+ return !!(cell->ce_mask & CELL_ATTR_NAME);
+}
+
+const char *nl_dect_cell_get_name(const struct nl_dect_cell *cell)
+{
+ return cell->c_name;
+}
+
+void nl_dect_cell_set_flags(struct nl_dect_cell *cell, uint32_t flags)
+{
+ cell->c_flags = flags;
+ cell->ce_mask |= CELL_ATTR_FLAGS;
+}
+
+bool nl_dect_cell_test_flags(const struct nl_dect_cell *cell)
+{
+ return !!(cell->ce_mask & CELL_ATTR_FLAGS);
+}
+
+uint32_t nl_dect_cell_get_flags(const struct nl_dect_cell *cell)
+{
+ return cell->c_flags;
+}
+
+void nl_dect_cell_set_transceiver(struct nl_dect_cell *cell, unsigned int i,
+ const char *id)
+{
+ cell->c_transceiver[i] = strdup(id);
+ cell->ce_mask |= CELL_ATTR_TRANSCEIVER;
+}
+
+bool nl_dect_cell_test_transceiver(const struct nl_dect_cell *cell)
+{
+ return !!(cell->ce_mask & CELL_ATTR_TRANSCEIVER);
+}
+
+const char *nl_dect_cell_get_transceiver(const struct nl_dect_cell *cell,
+ unsigned int i)
+{
+ return cell->c_transceiver[i];
+}
+
+void nl_dect_cell_set_link(struct nl_dect_cell *cell, int link)
+{
+ cell->c_link = link;
+ cell->ce_mask |= CELL_ATTR_LINK;
+}
+
+bool nl_dect_cell_test_link(const struct nl_dect_cell *cell)
+{
+ return !!(cell->ce_mask & CELL_ATTR_LINK);
+}
+
+int nl_dect_cell_get_link(const struct nl_dect_cell *cell)
+{
+ return cell->c_link;
+}
+
+int nl_dect_cell_build_msg(struct nl_msg *msg, struct nl_dect_cell *cell)
+{
+ struct dectmsg dm = {
+ .dm_index = cell->c_index,
+ };
+
+ if (nlmsg_append(msg, &dm, sizeof(dm), NLMSG_ALIGNTO) < 0)
+ goto nla_put_failure;
+ if (cell->ce_mask & CELL_ATTR_NAME)
+ NLA_PUT_STRING(msg, DECTA_CELL_NAME, cell->c_name);
+ if (cell->ce_mask & CELL_ATTR_FLAGS)
+ NLA_PUT_U32(msg, DECTA_CELL_FLAGS, cell->c_flags);
+ if (cell->ce_mask & CELL_ATTR_LINK)
+ NLA_PUT_U8(msg, DECTA_CELL_CLUSTER, cell->c_link);
+ return 0;
+
+nla_put_failure:
+ return -NLE_MSGSIZE;
+}
+
+static struct trans_tbl cell_flags[] = {
+ __ADD(DECT_CELL_CCP, ccp)
+ __ADD(DECT_CELL_SLAVE, slave)
+ __ADD(DECT_CELL_MONITOR, monitor)
+};
+
+char *nl_dect_cell_flags2str(uint32_t flags, char *buf, size_t len)
+{
+ return __flags2str(flags, buf, len, cell_flags, ARRAY_SIZE(cell_flags));
+}
+
+uint32_t nl_dect_cell_str2flags(const char *str)
+{
+ return __str2flags(str, cell_flags, ARRAY_SIZE(cell_flags));
+}
+
+/** @cond SKIP */
+struct nl_object_ops nl_dect_cell_obj_ops = {
+ .oo_name = "nl_dect/cell",
+ .oo_size = sizeof(struct nl_dect_cell),
+ .oo_free_data = cell_free_data,
+ .oo_dump = {
+ [NL_DUMP_LINE] = cell_dump,
+ },
+ .oo_id_attrs = CELL_ATTR_NAME,
+};
+
+/** @endcond */
+
+/** @} */
diff --git a/lib/dect/cluster.c b/lib/dect/cluster.c
new file mode 100644
index 0000000..29ac522
--- /dev/null
+++ b/lib/dect/cluster.c
@@ -0,0 +1,417 @@
+/*
+ * lib/dect/cluster.c DECT Cluster objects
+ *
+ * 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 version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
+ */
+
+/**
+ * @ingroup nl_dect
+ * @defgroup nl_dect Transceivers
+ * @brief
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/dect/cluster.h>
+#include <netlink/dect/ari.h>
+#include <linux/dect_netlink.h>
+
+/** @cond SKIP */
+static struct nl_cache_ops nl_dect_cluster_ops;
+/** @endcond */
+
+static struct nla_policy mbc_tb_policy[DECTA_MBC_TB_MAX + 1] = {
+ [DECTA_MBC_TB_LBN] = { .type = NLA_U8 },
+ [DECTA_MBC_TB_ECN] = { .type = NLA_U8 },
+ [DECTA_MBC_TB_CELL] = { .type = NLA_U8 },
+ [DECTA_MBC_TB_RX_SLOT] = { .type = NLA_U8 },
+ [DECTA_MBC_TB_TX_SLOT] = { .type = NLA_U8 },
+};
+
+static int tb_msg_parser(struct nlattr *nla, struct nl_dect_mbc *mbc)
+{
+ struct nlattr *tb[DECTA_MBC_TB_MAX + 1];
+ struct nl_dect_mbc_tb *mtb;
+ int err;
+
+ if (nla_type(nla) != DECTA_LIST_ELEM)
+ return -NLE_INVAL;
+
+ err = nla_parse_nested(tb, DECTA_MBC_TB_MAX, nla, mbc_tb_policy);
+ if (err < 0)
+ return err;
+
+ mtb = &mbc->mbc_tbs[mbc->mbc_ntbs++];
+ if (tb[DECTA_MBC_TB_LBN] != NULL)
+ mtb->mtb_lbn = nla_get_u8(tb[DECTA_MBC_TB_LBN]);
+ if (tb[DECTA_MBC_TB_ECN] != NULL)
+ mtb->mtb_ecn = nla_get_u8(tb[DECTA_MBC_TB_ECN]);
+ if (tb[DECTA_MBC_TB_CELL] != NULL)
+ mtb->mtb_cell = nla_get_u8(tb[DECTA_MBC_TB_CELL]);
+ if (tb[DECTA_MBC_TB_RX_SLOT] != NULL)
+ mtb->mtb_rx_slot = nla_get_u8(tb[DECTA_MBC_TB_RX_SLOT]);
+ if (tb[DECTA_MBC_TB_TX_SLOT] != NULL)
+ mtb->mtb_tx_slot = nla_get_u8(tb[DECTA_MBC_TB_TX_SLOT]);
+
+ return 0;
+}
+
+static struct nla_policy mbc_stats_policy[DECTA_MBC_STATS_MAX + 1] = {
+ [DECTA_MBC_STATS_CS_RX_BYTES] = { .type = NLA_U32 },
+ [DECTA_MBC_STATS_CS_TX_BYTES] = { .type = NLA_U32 },
+ [DECTA_MBC_STATS_I_RX_BYTES] = { .type = NLA_U32 },
+ [DECTA_MBC_STATS_I_TX_BYTES] = { .type = NLA_U32 },
+ [DECTA_MBC_STATS_HANDOVERS] = { .type = NLA_U32 },
+};
+
+static int mbc_stats_msg_parser(struct nlattr *nla, struct nl_dect_mbc *mbc)
+{
+ struct nlattr *tb[DECTA_MBC_STATS_MAX + 1];
+ int err;
+
+ err = nla_parse_nested(tb, DECTA_MBC_MAX, nla, mbc_stats_policy);
+ if (err < 0)
+ return err;
+
+ if (tb[DECTA_MBC_STATS_CS_RX_BYTES] != NULL)
+ mbc->mbc_cs_rx_bytes = nla_get_u32(tb[DECTA_MBC_STATS_CS_RX_BYTES]);
+ if (tb[DECTA_MBC_STATS_CS_TX_BYTES] != NULL)
+ mbc->mbc_cs_tx_bytes = nla_get_u32(tb[DECTA_MBC_STATS_CS_TX_BYTES]);
+ if (tb[DECTA_MBC_STATS_I_RX_BYTES] != NULL)
+ mbc->mbc_i_rx_bytes = nla_get_u32(tb[DECTA_MBC_STATS_I_RX_BYTES]);
+ if (tb[DECTA_MBC_STATS_I_TX_BYTES] != NULL)
+ mbc->mbc_i_tx_bytes = nla_get_u32(tb[DECTA_MBC_STATS_I_TX_BYTES]);
+ if (tb[DECTA_MBC_STATS_HANDOVERS] != NULL)
+ mbc->mbc_handovers = nla_get_u32(tb[DECTA_MBC_STATS_HANDOVERS]);
+ return 0;
+}
+
+static struct nla_policy mbc_policy[DECTA_MBC_MAX + 1] = {
+ [DECTA_MBC_MCEI] = { .type = NLA_U32 },
+ [DECTA_MBC_SERVICE] = { .type = NLA_U8 },
+ [DECTA_MBC_STATE] = { .type = NLA_U8 },
+ [DECTA_MBC_CIPHER_STATE] = { .type = NLA_U8 },
+ [DECTA_MBC_TBS] = { .type = NLA_NESTED },
+};
+
+static int mbc_msg_parser(struct nlattr *nla, struct nl_dect_cluster *cl)
+{
+ struct nlattr *tb[DECTA_MBC_MAX + 1];
+ struct nl_dect_mbc *mbc;
+ int err, rem;
+
+ if (nla_type(nla) != DECTA_LIST_ELEM)
+ return -NLE_INVAL;
+
+ err = nla_parse_nested(tb, DECTA_MBC_MAX, nla, mbc_policy);
+ if (err < 0)
+ return err;
+
+ mbc = &cl->cl_mbcs[cl->cl_nmbcs++];
+ if (tb[DECTA_MBC_MCEI] != NULL)
+ mbc->mbc_mcei = nla_get_u32(tb[DECTA_MBC_MCEI]);
+ if (tb[DECTA_MBC_SERVICE] != NULL)
+ mbc->mbc_service = nla_get_u8(tb[DECTA_MBC_SERVICE]);
+ if (tb[DECTA_MBC_STATE] != NULL)
+ mbc->mbc_state = nla_get_u8(tb[DECTA_MBC_STATE]);
+ if (tb[DECTA_MBC_CIPHER_STATE] != NULL)
+ mbc->mbc_cipher_state = nla_get_u8(tb[DECTA_MBC_CIPHER_STATE]);
+ if (tb[DECTA_MBC_STATS] != NULL) {
+ err = mbc_stats_msg_parser(tb[DECTA_MBC_STATS], mbc);
+ if (err < 0)
+ return err;
+ }
+ if (tb[DECTA_MBC_TBS] != NULL) {
+ nla_for_each_nested(nla, tb[DECTA_MBC_TBS], rem) {
+ err = tb_msg_parser(nla, mbc);
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+static struct nla_policy cluster_policy[DECTA_CLUSTER_MAX + 1] = {
+ [DECTA_CLUSTER_NAME] = { .type = NLA_STRING, .maxlen = 16 },
+ [DECTA_CLUSTER_MODE] = { .type = NLA_U8 },
+ [DECTA_CLUSTER_PARI] = { .type = NLA_NESTED },
+ [DECTA_CLUSTER_CELLS] = { .type = NLA_NESTED },
+ [DECTA_CLUSTER_MBCS] = { .type = NLA_NESTED },
+};
+
+static int cluster_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+ struct nlmsghdr *n, struct nl_parser_param *pp)
+{
+ struct dectmsg *dm = nlmsg_data(n);
+ struct nl_dect_cluster *cl;
+ struct nlattr *tb[DECTA_CLUSTER_MAX + 1], *nla;
+ int err, rem;
+
+ err = nlmsg_parse(n, sizeof(*dm), tb, DECTA_CLUSTER_MAX, cluster_policy);
+ if (err < 0)
+ return err;
+
+ cl = nl_dect_cluster_alloc();
+ if (cl == NULL) {
+ err = -NLE_NOMEM;
+ goto errout;
+ }
+
+ cl->ce_msgtype = n->nlmsg_type;
+ cl->cl_index = dm->dm_index;
+
+ if (tb[DECTA_CLUSTER_NAME] != NULL) {
+ char *name = nla_strdup(tb[DECTA_CLUSTER_NAME]);
+ if (name == NULL) {
+ err = -NLE_NOMEM;
+ goto errout;
+ }
+ nl_dect_cluster_set_name(cl, name);
+ free(name);
+ }
+
+ if (tb[DECTA_CLUSTER_MODE] != NULL)
+ nl_dect_cluster_set_mode(cl, nla_get_u8(tb[DECTA_CLUSTER_MODE]));
+
+ if (tb[DECTA_CLUSTER_PARI] != NULL) {
+ struct nl_dect_ari pari;
+
+ err = nl_dect_parse_ari(&pari, tb[DECTA_CLUSTER_PARI]);
+ if (err < 0)
+ goto errout;
+ nl_dect_cluster_set_pari(cl, &pari);
+ }
+
+ if (tb[DECTA_CLUSTER_MBCS] != NULL) {
+ nla_for_each_nested(nla, tb[DECTA_CLUSTER_MBCS], rem) {
+ err = mbc_msg_parser(nla, cl);
+ if (err < 0)
+ goto errout;
+ }
+ }
+
+ err = pp->pp_cb((struct nl_object *)cl, pp);
+errout:
+ nl_dect_cluster_put(cl);
+ return err;
+}
+
+static int cluster_request_update(struct nl_cache *c, struct nl_sock *h)
+{
+ struct dectmsg dm;
+
+ memset(&dm, 0, sizeof(dm));
+ return nl_send_simple(h, DECT_GET_CLUSTER, NLM_F_DUMP, &dm, sizeof(dm));
+}
+
+/**
+ * @name Cache Management
+ * @{
+ */
+int nl_dect_cluster_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
+{
+ struct nl_cache *cache;
+ int err;
+
+ cache = nl_cache_alloc(&nl_dect_cluster_ops);
+ if (cache == NULL)
+ return -NLE_NOMEM;
+
+ if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
+ free(cache);
+ return err;
+ }
+
+ *result = cache;
+ return 0;
+}
+
+struct nl_dect_cluster *nl_dect_cluster_get_by_name(struct nl_cache *cache,
+ const char *name)
+{
+ struct nl_dect_cluster *cl;
+
+ nl_list_for_each_entry(cl, &cache->c_items, ce_list) {
+ if (!strcmp(cl->cl_name, name)) {
+ nl_object_get((struct nl_object *)cl);
+ return cl;
+ }
+ }
+ return NULL;
+}
+
+struct nl_dect_cluster *nl_dect_cluster_get_by_index(struct nl_cache *cache, int index)
+{
+ struct nl_dect_cluster *cl;
+
+ nl_list_for_each_entry(cl, &cache->c_items, ce_list) {
+ if (cl->cl_index == index) {
+ nl_object_get((struct nl_object *)cl);
+ return cl;
+ }
+ }
+ return NULL;
+}
+
+/** @} */
+
+/**
+ * @name Device creation
+ * @{
+ */
+
+static int build_cluster_msg(struct nl_dect_cluster *tmpl, int cmd, int flags,
+ struct nl_msg **result)
+{
+ struct nl_msg *msg;
+ int err;
+
+ msg = nlmsg_alloc_simple(cmd, flags);
+ if (msg == NULL)
+ return -NLE_NOMEM;
+
+ err = nl_dect_cluster_build_msg(msg, tmpl);
+ if (err < 0) {
+ nlmsg_free(msg);
+ return err;
+ }
+
+ *result = msg;
+ return 0;
+}
+
+int nl_dect_cluster_build_add_request(struct nl_dect_cluster *tmpl, int flags,
+ struct nl_msg **result)
+{
+ return build_cluster_msg(tmpl, DECT_NEW_CLUSTER, NLM_F_CREATE | flags,
+ result);
+}
+
+int nl_dect_cluster_add(struct nl_sock *sk, struct nl_dect_cluster *cl, int flags)
+{
+ struct nl_msg *msg;
+ int err;
+
+ err = nl_dect_cluster_build_add_request(cl, flags, &msg);
+ if (err < 0)
+ return err;
+
+ err = nl_send_auto_complete(sk, msg);
+ nlmsg_free(msg);
+ if (err < 0)
+ return err;
+
+ return wait_for_ack(sk);
+}
+
+int nl_dect_cluster_build_del_request(struct nl_dect_cluster *tmpl, int flags,
+ struct nl_msg **result)
+{
+ return build_cluster_msg(tmpl, DECT_DEL_CLUSTER, flags, result);
+}
+
+int nl_dect_cluster_delete(struct nl_sock *sk, struct nl_dect_cluster *tmpl, int flags)
+{
+ struct nl_msg *msg;
+ int err;
+
+ err = nl_dect_cluster_build_del_request(tmpl, flags, &msg);
+ if (err < 0)
+ return err;
+
+ err = nl_send_auto_complete(sk, msg);
+ nlmsg_free(msg);
+ if (err < 0)
+ return err;
+
+ return wait_for_ack(sk);
+}
+
+int nl_dect_cluster_build_query_request(struct nl_dect_cluster *tmpl, int flags,
+ struct nl_msg **result)
+{
+ return build_cluster_msg(tmpl, DECT_GET_CLUSTER, flags, result);
+}
+
+int nl_dect_cluster_query(struct nl_sock *sk, struct nl_dect_cluster *cl, int flags)
+{
+ struct nl_msg *msg;
+ int err;
+
+ err = nl_dect_cluster_build_query_request(cl, flags, &msg);
+ if (err < 0)
+ return err;
+
+ err = nl_send_auto_complete(sk, msg);
+ nlmsg_free(msg);
+ if (err < 0)
+ return err;
+
+ return wait_for_ack(sk);
+}
+
+/** @} */
+
+/**
+ * @name Name <-> Index Translations
+ * @{
+ */
+
+char *nl_dect_cluster_i2name(struct nl_cache *cache, int index,
+ char *buf, size_t len)
+{
+ struct nl_dect_cluster *cl = nl_dect_cluster_get_by_index(cache, index);
+
+ if (cl != NULL) {
+ strncpy(buf, cl->cl_name, len - 1);
+ buf[len - 1] = 0;
+ return buf;
+ }
+
+ return NULL;
+}
+
+int nl_dect_cluster_name2i(struct nl_cache *cache, const char *name)
+{
+ struct nl_dect_cluster *cl = nl_dect_cluster_get_by_name(cache, name);
+
+ if (cl != NULL)
+ return cl->cl_index;
+ return 0;
+}
+
+/** @} */
+
+static struct nl_cache_ops nl_dect_cluster_ops = {
+ .co_name = "nl_dect/cluster",
+ .co_hdrsize = 0,
+ .co_msgtypes = {
+ { DECT_NEW_CLUSTER, NL_ACT_NEW, "new" },
+ { DECT_DEL_CLUSTER, NL_ACT_NEW, "del" },
+ { DECT_GET_CLUSTER, NL_ACT_GET, "get" },
+ END_OF_MSGTYPES_LIST
+ },
+ .co_protocol = NETLINK_DECT,
+ .co_request_update = cluster_request_update,
+ .co_msg_parser = cluster_msg_parser,
+ .co_obj_ops = &nl_dect_cluster_obj_ops,
+};
+
+static void __init cluster_init(void)
+{
+ nl_cache_mngt_register(&nl_dect_cluster_ops);
+}
+
+static void __exit cluster_exit(void)
+{
+ nl_cache_mngt_unregister(&nl_dect_cluster_ops);
+}
+
+/** @} */
diff --git a/lib/dect/cluster_obj.c b/lib/dect/cluster_obj.c
new file mode 100644
index 0000000..043c677
--- /dev/null
+++ b/lib/dect/cluster_obj.c
@@ -0,0 +1,274 @@
+/*
+ * lib/dect/cluster.c DECT Cluster objects
+ *
+ * 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 version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/utils.h>
+#include <netlink/data.h>
+#include <netlink/dect/cluster.h>
+#include <netlink/dect/ari.h>
+#include <linux/dect_netlink.h>
+#include <linux/dect.h>
+
+/** @cond SKIP */
+#define CL_ATTR_NAME 0x0001
+#define CL_ATTR_MODE 0x0002
+#define CL_ATTR_PARI 0x0004
+/** @endcond */
+
+char *nl_dect_mbc_state2string(enum dect_mbc_state state,
+ char *buf, size_t len);
+char *nl_dect_mbc_service2string(enum dect_mac_service_types type,
+ char *buf, size_t len);
+char *nl_dect_mbc_cipher2string(enum dect_cipher_states state,
+ char *buf, size_t len);
+
+static void cluster_free_data(struct nl_object *obj)
+{
+ struct nl_dect_cluster *cl = nl_object_priv(obj);
+
+ if (cl == NULL)
+ return;
+ free(cl->cl_name);
+}
+
+static void mbc_tb_dump(struct nl_dect_mbc_tb *mtb, struct nl_dump_params *p)
+{
+ nl_dump(p, "\t TB: LBN: %u ECN: %u Cell: %u RX/TX-Slots: %u/%u\n",
+ mtb->mtb_lbn, mtb->mtb_ecn,
+ mtb->mtb_cell, mtb->mtb_rx_slot, mtb->mtb_tx_slot);
+}
+
+static void mbc_dump(struct nl_dect_mbc *mbc, struct nl_dump_params *p)
+{
+ unsigned int i;
+ char buf[64];
+
+ nl_dump(p, "\tMBC: MCEI %u\n", mbc->mbc_mcei);
+ nl_dump(p, "\t State: %s\n",
+ nl_dect_mbc_state2string(mbc->mbc_state,
+ buf, sizeof(buf)));
+ nl_dump(p, "\t Service: %s\n",
+ nl_dect_mbc_service2string(mbc->mbc_service,
+ buf, sizeof(buf)));
+ nl_dump(p, "\t Cipher state: %s\n",
+ nl_dect_mbc_cipher2string(mbc->mbc_cipher_state,
+ buf, sizeof(buf)));
+ nl_dump(p, "\t Cs-channel RX-Bytes: %u TX-Bytes: %u\n",
+ mbc->mbc_cs_rx_bytes, mbc->mbc_cs_tx_bytes);
+ nl_dump(p, "\t I-channel RX-Bytes: %u TX-Bytes: %u\n",
+ mbc->mbc_i_rx_bytes, mbc->mbc_i_tx_bytes);
+
+ for (i = 0; i < mbc->mbc_ntbs; i++)
+ mbc_tb_dump(&mbc->mbc_tbs[i], p);
+}
+
+static void cluster_dump(struct nl_object *obj, struct nl_dump_params *p)
+{
+ struct nl_dect_cluster *cl = nl_object_priv(obj);
+ unsigned int i;
+ char buf[64];
+
+ if (cl->ce_mask & CL_ATTR_NAME)
+ nl_dump_line(p, "%d: DECT Cluster %s:\n",
+ cl->cl_index, cl->cl_name);
+
+ if (cl->ce_mask & CL_ATTR_MODE)
+ nl_dump_line(p, "\tMode: %s\n",
+ nl_dect_cluster_mode2str(cl->cl_mode,
+ buf, sizeof(buf)));
+
+ if (cl->ce_mask & CL_ATTR_PARI) {
+ nl_dump(p, "\tPARI: ");
+ nl_dect_dump_ari(&cl->cl_pari, p);
+ nl_dump(p, "\n");
+ }
+
+ nl_dump(p, "\n");
+ for (i = 0; i < cl->cl_nmbcs; i++)
+ mbc_dump(&cl->cl_mbcs[i], p);
+}
+
+/**
+ * @name Allocation/Freeing
+ * @{
+ */
+
+struct nl_dect_cluster *nl_dect_cluster_alloc(void)
+{
+ return (struct nl_dect_cluster *)nl_object_alloc(&nl_dect_cluster_obj_ops);
+}
+
+void nl_dect_cluster_get(struct nl_dect_cluster *cl)
+{
+ nl_object_get((struct nl_object *)cl);
+}
+
+void nl_dect_cluster_put(struct nl_dect_cluster *cl)
+{
+ nl_object_put((struct nl_object *)cl);
+}
+
+/** @} */
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+unsigned int nl_dect_cluster_get_index(const struct nl_dect_cluster *cl)
+{
+ return cl->cl_index;
+}
+
+void nl_dect_cluster_set_name(struct nl_dect_cluster *cl, const char *name)
+{
+ cl->cl_name = strdup(name);
+ cl->ce_mask |= CL_ATTR_NAME;
+}
+
+bool nl_dect_cluster_test_name(const struct nl_dect_cluster *cl)
+{
+ return !!(cl->ce_mask & CL_ATTR_NAME);
+}
+
+const char *nl_dect_cluster_get_name(const struct nl_dect_cluster *cl)
+{
+ return cl->cl_name;
+}
+
+void nl_dect_cluster_set_mode(struct nl_dect_cluster *cl, uint8_t mode)
+{
+ cl->cl_mode = mode;
+ cl->ce_mask |= CL_ATTR_MODE;
+}
+
+bool nl_dect_cluster_test_mode(const struct nl_dect_cluster *cl)
+{
+ return !!(cl->ce_mask & CL_ATTR_MODE);
+}
+
+uint8_t nl_dect_cluster_get_mode(const struct nl_dect_cluster *cl)
+{
+ return cl->cl_mode;
+}
+
+void nl_dect_cluster_set_pari(struct nl_dect_cluster *cl, const struct nl_dect_ari *pari)
+{
+ memcpy(&cl->cl_pari, pari, sizeof(cl->cl_pari));
+ cl->ce_mask |= CL_ATTR_PARI;
+}
+
+bool nl_dect_cluster_test_pari(const struct nl_dect_cluster *cl)
+{
+ return !!(cl->ce_mask & CL_ATTR_PARI);
+}
+
+const struct nl_dect_ari *nl_dect_cluster_get_pari(const struct nl_dect_cluster *cl)
+{
+ return &cl->cl_pari;
+}
+
+int nl_dect_cluster_build_msg(struct nl_msg *msg, struct nl_dect_cluster *cl)
+{
+ struct dectmsg dm = {
+ .dm_index = cl->cl_index,
+ };
+
+ if (nlmsg_append(msg, &dm, sizeof(dm), NLMSG_ALIGNTO) < 0)
+ goto nla_put_failure;
+
+ if (cl->ce_mask & CL_ATTR_NAME)
+ NLA_PUT_STRING(msg, DECTA_CLUSTER_NAME, cl->cl_name);
+ if (nl_dect_fill_ari(msg, &cl->cl_pari, DECTA_CLUSTER_PARI) < 0)
+ goto nla_put_failure;
+ if (cl->ce_mask & CL_ATTR_MODE)
+ NLA_PUT_U8(msg, DECTA_CLUSTER_MODE, cl->cl_mode);
+ return 0;
+
+nla_put_failure:
+ return -NLE_MSGSIZE;
+}
+
+static struct trans_tbl cluster_modes[] = {
+ __ADD(DECT_MODE_FP, FP)
+ __ADD(DECT_MODE_PP, PP)
+};
+
+char *nl_dect_cluster_mode2str(enum dect_cluster_modes mode, char *buf, size_t len)
+{
+ return __type2str(mode, buf, len, cluster_modes,
+ ARRAY_SIZE(cluster_modes));
+}
+
+enum dect_cluster_modes nl_dect_cluster_str2mode(const char *str)
+{
+ return __str2type(str, cluster_modes, ARRAY_SIZE(cluster_modes));
+}
+
+static struct trans_tbl mbc_states[] = {
+ __ADD(DECT_MBC_NONE, none)
+ __ADD(DECT_MBC_INITIATED, initiated)
+ __ADD(DECT_MBC_ESTABLISHED, established)
+ __ADD(DECT_MBC_RELEASED, released)
+};
+
+char *nl_dect_mbc_state2string(enum dect_mbc_state state,
+ char *buf, size_t len)
+{
+ return __type2str(state, buf, len, mbc_states,
+ ARRAY_SIZE(mbc_states));
+}
+
+static struct trans_tbl mac_service_types[] = {
+ __ADD(DECT_SERVICE_IN_MIN_DELAY, IN_min_delay)
+ __ADD(DECT_SERVICE_IPX_ENCODED_PROTECTED, IPX_encoded_protected)
+ __ADD(DECT_SERVICE_IN_NORMAL_DELAY, IN_normal_delay)
+ __ADD(DECT_SERVICE_UNKNOWN, unknown)
+ __ADD(DECT_SERVICE_C_CHANNEL_ONLY, C_channel_only)
+ __ADD(DECT_SERVICE_IP_ERROR_DETECTION, IP_error_detection)
+ __ADD(DECT_SERVICE_IPQ_ERROR_DETECTION, IPQ_error_detection)
+};
+
+char *nl_dect_mbc_service2string(enum dect_mac_service_types type,
+ char *buf, size_t len)
+{
+ return __type2str(type, buf, len, mac_service_types,
+ ARRAY_SIZE(mac_service_types));
+}
+
+static struct trans_tbl cipher_states[] = {
+ __ADD(DECT_CIPHER_DISABLED, disabled)
+ __ADD(DECT_CIPHER_ENABLED, enabled)
+};
+
+char *nl_dect_mbc_cipher2string(enum dect_cipher_states state,
+ char *buf, size_t len)
+{
+ return __type2str(state, buf, len, cipher_states,
+ ARRAY_SIZE(cipher_states));
+}
+
+/** @cond SKIP */
+struct nl_object_ops nl_dect_cluster_obj_ops = {
+ .oo_name = "nl_dect/cluster",
+ .oo_size = sizeof(struct nl_dect_cluster),
+ .oo_free_data = cluster_free_data,
+ .oo_dump = {
+ [NL_DUMP_LINE] = cluster_dump,
+ },
+ .oo_id_attrs = CL_ATTR_NAME,
+};
+
+/** @endcond */
+
+/** @} */
diff --git a/lib/dect/dect.c b/lib/dect/dect.c
new file mode 100644
index 0000000..c7fbc18
--- /dev/null
+++ b/lib/dect/dect.c
@@ -0,0 +1,22 @@
+/*
+ * lib/dect/dect.c DECT
+ *
+ * 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 version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/dect/dect.h>
+
+double nl_dect_rssi_to_dbm(uint8_t rssi)
+{
+ if (rssi == 0)
+ return 0;
+ return -93 + (rssi * 60.0 / 255);
+}
diff --git a/lib/dect/llme.c b/lib/dect/llme.c
new file mode 100644
index 0000000..baed29e
--- /dev/null
+++ b/lib/dect/llme.c
@@ -0,0 +1,776 @@
+/*
+ * lib/dect/llme.c DECT Lower Layer Management Entity Objects
+ *
+ * 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 version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
+ */
+
+/**
+ * @ingroup dect
+ * @defgroup dect LLME
+ * @brief
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/dect/dect.h>
+#include <netlink/dect/ari.h>
+#include <netlink/dect/llme.h>
+#include <linux/dect_netlink.h>
+
+/** @cond SKIP */
+static struct nl_object_ops llme_msg_obj_ops;
+/** @endcond */
+
+#define MAC_INFO_ATTR_PARI 0x0010000
+#define MAC_INFO_ATTR_RPN 0x0020000
+#define MAC_INFO_ATTR_RSSI 0x0040000
+#define MAC_INFO_ATTR_FPC 0x0080000
+#define MAC_INFO_ATTR_HLC 0x0100000
+#define MAC_INFO_ATTR_EFPC 0x0200000
+#define MAC_INFO_ATTR_EHLC 0x0400000
+#define MAC_INFO_ATTR_EFPC2 0x0800000
+#define MAC_INFO_ATTR_EHLC2 0x1000000
+#define MAC_INFO_ATTR_MFN 0x2000000
+
+static inline struct nl_dect_llme_mac_info *mac_info(const struct nl_dect_llme_msg *lmsg)
+{
+ return (void *)&lmsg->lm_mi;
+}
+
+void nl_dect_llme_mac_info_set_pari(struct nl_dect_llme_msg *lmsg,
+ const struct nl_dect_ari *pari)
+{
+ struct nl_dect_llme_mac_info *mi = mac_info(lmsg);
+
+ memcpy(&mi->mi_pari, pari, sizeof(mi->mi_pari));
+ lmsg->ce_mask |= MAC_INFO_ATTR_PARI;
+}
+
+bool nl_dect_llme_mac_info_test_pari(const struct nl_dect_llme_msg *lmsg)
+{
+ return !!(lmsg->ce_mask & MAC_INFO_ATTR_PARI);
+}
+
+const struct nl_dect_ari *nl_dect_llme_mac_info_get_pari(const struct nl_dect_llme_msg *lmsg)
+{
+ return &mac_info(lmsg)->mi_pari;
+}
+
+void nl_dect_llme_mac_info_set_rpn(struct nl_dect_llme_msg *lmsg, uint8_t rpn)
+{
+ mac_info(lmsg)->mi_rpn = rpn;
+ lmsg->ce_mask |= MAC_INFO_ATTR_RPN;
+}
+
+uint8_t nl_dect_llme_mac_info_get_rpn(const struct nl_dect_llme_msg *lmsg)
+{
+ return mac_info(lmsg)->mi_rpn;
+}
+
+void nl_dect_llme_mac_info_set_rssi(struct nl_dect_llme_msg *lmsg, uint8_t rssi)
+{
+ mac_info(lmsg)->mi_rssi = rssi;
+ lmsg->ce_mask |= MAC_INFO_ATTR_RSSI;
+}
+
+uint8_t nl_dect_llme_mac_info_get_rssi(const struct nl_dect_llme_msg *lmsg)
+{
+ return mac_info(lmsg)->mi_rssi;
+}
+
+void nl_dect_llme_mac_info_set_fpc(struct nl_dect_llme_msg *lmsg, uint32_t fpc)
+{
+ mac_info(lmsg)->mi_fpc = fpc;
+ lmsg->ce_mask |= MAC_INFO_ATTR_FPC;
+}
+
+uint32_t nl_dect_llme_mac_info_get_fpc(const struct nl_dect_llme_msg *lmsg)
+{
+ return mac_info(lmsg)->mi_fpc;
+}
+
+void nl_dect_llme_mac_info_set_hlc(struct nl_dect_llme_msg *lmsg, uint16_t hlc)
+{
+ mac_info(lmsg)->mi_hlc = hlc;
+ lmsg->ce_mask |= MAC_INFO_ATTR_HLC;
+}
+
+uint16_t nl_dect_llme_mac_info_get_hlc(const struct nl_dect_llme_msg *lmsg)
+{
+ return mac_info(lmsg)->mi_hlc;
+}
+
+void nl_dect_llme_mac_info_set_efpc(struct nl_dect_llme_msg *lmsg, uint16_t efpc)
+{
+ mac_info(lmsg)->mi_efpc = efpc;
+ lmsg->ce_mask |= MAC_INFO_ATTR_EFPC;
+}
+
+uint16_t nl_dect_llme_mac_info_get_efpc(const struct nl_dect_llme_msg *lmsg)
+{
+ return mac_info(lmsg)->mi_efpc;
+}
+
+void nl_dect_llme_mac_info_set_ehlc(struct nl_dect_llme_msg *lmsg, uint32_t ehlc)
+{
+ mac_info(lmsg)->mi_ehlc = ehlc;
+ lmsg->ce_mask |= MAC_INFO_ATTR_EHLC;
+}
+
+uint32_t nl_dect_llme_mac_info_get_ehlc(const struct nl_dect_llme_msg *lmsg)
+{
+ return mac_info(lmsg)->mi_ehlc;
+}
+
+void nl_dect_llme_mac_info_set_efpc2(struct nl_dect_llme_msg *lmsg, uint16_t efpc2)
+{
+ mac_info(lmsg)->mi_efpc2 = efpc2;
+ lmsg->ce_mask |= MAC_INFO_ATTR_EFPC2;
+}
+
+uint16_t nl_dect_llme_mac_info_get_efpc2(const struct nl_dect_llme_msg *lmsg)
+{
+ return mac_info(lmsg)->mi_efpc2;
+}
+
+void nl_dect_llme_mac_info_set_ehlc2(struct nl_dect_llme_msg *lmsg, uint32_t ehlc2)
+{
+ mac_info(lmsg)->mi_ehlc2 = ehlc2;
+ lmsg->ce_mask |= MAC_INFO_ATTR_EHLC2;
+}
+
+uint32_t nl_dect_llme_mac_info_get_ehlc2(const struct nl_dect_llme_msg *lmsg)
+{
+ return mac_info(lmsg)->mi_ehlc2;
+}
+
+void nl_dect_llme_mac_info_set_mfn(struct nl_dect_llme_msg *lmsg, uint32_t mfn)
+{
+ mac_info(lmsg)->mi_mfn = mfn;
+ lmsg->ce_mask |= MAC_INFO_ATTR_MFN;
+}
+
+uint32_t nl_dect_llme_mac_info_get_mfn(const struct nl_dect_llme_msg *lmsg)
+{
+ return mac_info(lmsg)->mi_mfn;
+}
+
+
+static struct trans_tbl fixed_part_capabilities[] = {
+ __ADD(DECT_FPC_EXTENDED_FP_INFO, extended_fp_info)
+ __ADD(DECT_FPC_DOUBLE_DUPLEX_BEARER_CONNECTION, double_duplex_bearer_connection)
+ __ADD(DECT_FPC_RESERVED, reserved)
+ __ADD(DECT_FPC_DOUBLE_SLOT, double_slot)
+ __ADD(DECT_FPC_HALF_SLOT, half_slot)
+ __ADD(DECT_FPC_FULL_SLOT, full_slot)
+ __ADD(DECT_FPC_FREQ_CONTROL, frequency_control)
+ __ADD(DECT_FPC_PAGE_REPETITION, page_repetition)
+ __ADD(DECT_FPC_CO_SETUP_ON_DUMMY, co_setup_on_dummy)
+ __ADD(DECT_FPC_CL_UPLINK, cl_uplink)
+ __ADD(DECT_FPC_CL_DOWNLINK, cl_downlink)
+ __ADD(DECT_FPC_BASIC_A_FIELD_SETUP, basic_a_field_setup)
+ __ADD(DECT_FPC_ADV_A_FIELD_SETUP, advanced_a_field_setup)
+ __ADD(DECT_FPC_B_FIELD_SETUP, b_field_setup)
+ __ADD(DECT_FPC_CF_MESSAGES, cf_messages)
+ __ADD(DECT_FPC_IN_MIN_DELAY, in_min_delay)
+ __ADD(DECT_FPC_IN_NORM_DELAY, in_normal_delay)
+ __ADD(DECT_FPC_IP_ERROR_DETECTION, ip_error_detection)
+ __ADD(DECT_FPC_IP_ERROR_CORRECTION, ip_error_correction)
+ __ADD(DECT_FPC_MULTIBEARER_CONNECTIONS, multibearer_connections)
+};
+
+char *nl_dect_llme_fpc2str(uint32_t fpc, char *buf, size_t len)
+{
+ return __flags2str(fpc, buf, len, fixed_part_capabilities,
+ ARRAY_SIZE(fixed_part_capabilities));
+}
+
+uint32_t nl_dect_llme_str2fpc(const char *str)
+{
+ return __str2flags(str, fixed_part_capabilities,
+ ARRAY_SIZE(fixed_part_capabilities));
+}
+
+static struct trans_tbl higher_layer_capabilities[] = {
+ __ADD(DECT_HLC_ADPCM_G721_VOICE, adpcm_g721_voice)
+ __ADD(DECT_HLC_GAP_PAP_BASIC_SPEECH, gap_pap_basic_speech)
+ __ADD(DECT_HLC_NON_VOICE_CIRCUIT_SWITCHED, non_voice_circuit_switched_service)
+ __ADD(DECT_HLC_NON_VOICE_PACKET_SWITCHED, non_voice_packet_switched_service)
+ __ADD(DECT_HLC_STANDARD_AUTHENTICATION, standard_authentication)
+ __ADD(DECT_HLC_STANDARD_CIPHERING, standard_ciphering)
+ __ADD(DECT_HLC_LOCATION_REGISTRATION, location_registration)
+ __ADD(DECT_HLC_SIM_SERVICES, sim_services)
+ __ADD(DECT_HLC_NON_STATIC_FIXED_PART, non_static_fixed_part)
+ __ADD(DECT_HLC_CISS_SERVICE, ciss_service)
+ __ADD(DECT_HLC_CLMS_SERVICE, clms_service)
+ __ADD(DECT_HLC_COMS_SERVICE, coms_service)
+ __ADD(DECT_HLC_ACCESS_RIGHTS_REQUESTS, access_rights_requests)
+ __ADD(DECT_HLC_EXTERNAL_HANDOVER, external_handover)
+ __ADD(DECT_HLC_CONNECTION_HANDOVER, connection_handover)
+ __ADD(DECT_HLC_RESERVED, reserved)
+};
+
+char *nl_dect_llme_hlc2str(uint16_t hlc, char *buf, size_t len)
+{
+ return __flags2str(hlc, buf, len, higher_layer_capabilities,
+ ARRAY_SIZE(higher_layer_capabilities));
+}
+
+uint16_t nl_dect_llme_str2hlc(const char *str)
+{
+ return __str2flags(str, higher_layer_capabilities,
+ ARRAY_SIZE(higher_layer_capabilities));
+}
+
+static struct trans_tbl extended_fixed_part_capabilities[] = {
+ __ADD(DECT_EFPC_SYNC_PROLONGED_PREAMBLE, prolonged_preamble)
+ __ADD(DECT_EFPC_MAC_SUSPEND_RESUME, suspend_resume)
+ __ADD(DECT_EFPC_MAC_IP_Q_SERVICE, ip_q_service)
+ __ADD(DECT_EFPC_EXTENDED_FP_INFO2, extended_fp_info2)
+};
+
+char *nl_dect_llme_efpc2str(uint16_t efpc, char *buf, size_t len)
+{
+ return __flags2str(efpc, buf, len, extended_fixed_part_capabilities,
+ ARRAY_SIZE(extended_fixed_part_capabilities));
+}
+
+uint16_t nl_dect_llme_str2efpc(const char *str)
+{
+ return __str2flags(str, extended_fixed_part_capabilities,
+ ARRAY_SIZE(extended_fixed_part_capabilities));
+}
+static struct trans_tbl extended_higher_layer_capabilities[] = {
+ __ADD(DECT_EHLC_ISDN_DATA_SERVICE, isdn_data_service)
+ __ADD(DECT_EHLC_DPRS_FREL, dprs_frel)
+ __ADD(DECT_EHLC_DPRS_STREAM, dprs_stream)
+ __ADD(DECT_EHLC_DATA_SERVICE_PROFILE_D, data_service_profile_d)
+ __ADD(DECT_EHLC_LRMS, lrms)
+ __ADD(DECT_EHLC_ASYMETRIC_BEARERS, asymetric_bearers)
+ __ADD(DECT_EHLC_EMERGENCY_CALLS, emergency_calls)
+ __ADD(DECT_EHLC_TPUI_LOCATION_REGISTRATION, tpui_location_registration)
+ __ADD(DECT_EHLC_GPS_SYNCHRONIZED, gps_synchronized)
+ __ADD(DECT_EHLC_ISDN_INTERMEDIATE_SYSTEM, isdn_intermediate_system)
+ __ADD(DECT_EHLC_RAP_PART_1_PROFILE, rap_1_profile)
+ __ADD(DECT_EHLC_V_24, v_24)
+ __ADD(DECT_EHLC_PPP, ppp)
+ __ADD(DECT_EHLC_IP, ip)
+ __ADD(DECT_EHLC_TOKEN_RING, token_ring)
+ __ADD(DECT_EHLC_ETHERNET, ethernet)
+ __ADD(DECT_EHLC_IP_ROAMING, ip_roaming)
+ __ADD(DECT_EHLC_GENERIC_MEDIA_ENCAPSULATION, generic_media_encapsulation)
+ __ADD(DECT_EHLC_BASIC_ODAP, basic_odap)
+ __ADD(DECT_EHLC_F_MMS_INTERWORKING_PROFILE, mms_interworking_profile)
+};
+
+char *nl_dect_llme_ehlc2str(uint32_t ehlc, char *buf, size_t len)
+{
+ return __flags2str(ehlc, buf, len, extended_higher_layer_capabilities,
+ ARRAY_SIZE(extended_higher_layer_capabilities));
+}
+
+uint32_t nl_dect_llme_str2ehlc(const char *str)
+{
+ return __str2flags(str, extended_higher_layer_capabilities,
+ ARRAY_SIZE(extended_higher_layer_capabilities));
+}
+
+static struct trans_tbl extended_fixed_part_capabilities2[] = {
+ __ADD(DECT_EFPC2_NO_EMISSION_CARRIER, no_emission_carrier)
+ __ADD(DECT_EFPC2_GF, gf_channel)
+ __ADD(DECT_EFPC2_SI_PF, si_pf_channel)
+ __ADD(DECT_EFPC2_IP_F, ip_f_channel)
+ __ADD(DECT_EFPC2_LONG_SLOT_J672, long_slot_j672)
+ __ADD(DECT_EFPC2_LONG_SLOT_J640, long_slot_j640)
+};
+
+char *nl_dect_llme_efpc22str(uint16_t efpc2, char *buf, size_t len)
+{
+ return __flags2str(efpc2, buf, len, extended_fixed_part_capabilities2,
+ ARRAY_SIZE(extended_fixed_part_capabilities2));
+}
+
+uint16_t nl_dect_llme_str2efpc2(const char *str)
+{
+ return __str2flags(str, extended_fixed_part_capabilities2,
+ ARRAY_SIZE(extended_fixed_part_capabilities2));
+}
+
+static struct trans_tbl extended_higher_layer_capabilities2[] = {
+ __ADD(DECT_EHLC2_NG_DECT_PERMANENT_CLIR, permanent_clir)
+ __ADD(DECT_EHLC2_NG_DECT_MULTIPLE_CALLS, multiple_calls)
+ __ADD(DECT_EHLC2_NG_DECT_MULTIPLE_LINES, multiple_lines)
+ __ADD(DECT_EHLC2_EASY_PAIRING, easy_pairing)
+ __ADD(DECT_EHLC2_LIST_ACCESS_FEATURES, list_access_features)
+ __ADD(DECT_EHLC2_NO_EMISSION_MODE, no_emission_mode)
+ __ADD(DECT_EHLC2_NG_DECT_CALL_DEFLECTION, call_deflection)
+ __ADD(DECT_EHLC2_NG_DECT_INTRUSION_CALL, intrusion_call)
+ __ADD(DECT_EHLC2_NG_DECT_CONFERENCE_CALL, conference_call)
+ __ADD(DECT_EHLC2_NG_DECT_PARALLEL_CALLS, parallel_calls)
+ __ADD(DECT_EHLC2_NG_DECT_CALL_TRANSFER, call_transfer)
+ __ADD(DECT_EHLC2_NG_DECT_EXTENDED_WIDEBAND, extended_wideband)
+ __ADD(DECT_EHLC2_PACKET_DATA_CATEGORY_MASK, packet_data)
+ __ADD(DECT_EHLC2_NG_DECT_WIDEBAND, wideband)
+};
+
+char *nl_dect_llme_ehlc22str(uint32_t ehlc2, char *buf, size_t len)
+{
+ return __flags2str(ehlc2, buf, len, extended_higher_layer_capabilities2,
+ ARRAY_SIZE(extended_higher_layer_capabilities2));
+}
+
+uint32_t nl_dect_llme_str22ehlc(const char *str)
+{
+ return __str2flags(str, extended_higher_layer_capabilities2,
+ ARRAY_SIZE(extended_higher_layer_capabilities2));
+}
+
+
+static void nl_dect_llme_mac_info_dump(const struct nl_dect_llme_msg *lmsg,
+ struct nl_dump_params *p)
+{
+ const struct nl_dect_llme_mac_info *mi = mac_info(lmsg);
+ char buf[256];
+
+ if (lmsg->ce_mask & MAC_INFO_ATTR_PARI) {
+ nl_dump(p, "\n\tARI: ");
+ nl_dect_dump_ari(&mi->mi_pari, p);
+ }
+ if (lmsg->ce_mask & MAC_INFO_ATTR_RPN)
+ nl_dump(p, " RPN: %x", mi->mi_rpn);
+ if (lmsg->ce_mask & MAC_INFO_ATTR_RSSI)
+ nl_dump(p, " signal level: %.2fdBm", nl_dect_rssi_to_dbm(mi->mi_rssi));
+ if (lmsg->ce_mask & MAC_INFO_ATTR_FPC && mi->mi_fpc) {
+ nl_dect_llme_fpc2str(mi->mi_fpc, buf, sizeof(buf));
+ nl_dump(p, "\n\tMAC layer capabilities: %s", buf);
+ }
+ if (lmsg->ce_mask & MAC_INFO_ATTR_EFPC && mi->mi_efpc) {
+ nl_dect_llme_efpc2str(mi->mi_efpc, buf, sizeof(buf));
+ nl_dump(p, "\n\tExtended MAC layer capabilities: %s", buf);
+ }
+ if (lmsg->ce_mask & MAC_INFO_ATTR_EFPC2 && mi->mi_efpc2) {
+ nl_dect_llme_efpc22str(mi->mi_efpc2, buf, sizeof(buf));
+ nl_dump(p, "\n\tExtended MAC layer capabilities 2: %s", buf);
+ }
+ if (lmsg->ce_mask & MAC_INFO_ATTR_HLC && mi->mi_hlc) {
+ nl_dect_llme_hlc2str(mi->mi_hlc, buf, sizeof(buf));
+ nl_dump(p, "\n\tHigher layer capabilities: %s", buf);
+ }
+ if (lmsg->ce_mask & MAC_INFO_ATTR_EHLC && mi->mi_ehlc) {
+ nl_dect_llme_ehlc2str(mi->mi_ehlc, buf, sizeof(buf));
+ nl_dump(p, "\n\tExtended higher layer capabilities: %s", buf);
+ }
+ if (lmsg->ce_mask & MAC_INFO_ATTR_EHLC2 && mi->mi_ehlc2) {
+ nl_dect_llme_ehlc22str(mi->mi_ehlc2, buf, sizeof(buf));
+ nl_dump(p, "\n\tExtended higher layer capabilities 2: %s", buf);
+ }
+ nl_dump(p, "\n");
+}
+
+
+static struct nla_policy nl_dect_mac_info_policy[DECTA_MAC_INFO_MAX + 1] = {
+ [DECTA_MAC_INFO_PARI] = { .type = NLA_NESTED },
+ [DECTA_MAC_INFO_RPN] = { .type = NLA_U8 },
+ [DECTA_MAC_INFO_RSSI] = { .type = NLA_U8 },
+ [DECTA_MAC_INFO_SARI_LIST] = { .type = NLA_NESTED },
+ [DECTA_MAC_INFO_FPC] = { .type = NLA_U32 },
+ [DECTA_MAC_INFO_HLC] = { .type = NLA_U16 },
+ [DECTA_MAC_INFO_EFPC] = { .type = NLA_U16 },
+ [DECTA_MAC_INFO_EHLC] = { .type = NLA_U32 },
+ [DECTA_MAC_INFO_EFPC2] = { .type = NLA_U16 },
+ [DECTA_MAC_INFO_EHLC2] = { .type = NLA_U32 },
+ [DECTA_MAC_INFO_MFN] = { .type = NLA_U32 },
+};
+
+static int nl_dect_llme_mac_info_parse(struct nl_dect_llme_msg *lmsg,
+ struct nlattr *tb[])
+{
+ struct nl_dect_ari pari;
+ int err;
+
+ if (tb[DECTA_MAC_INFO_PARI] != NULL) {
+ err = nl_dect_parse_ari(&pari, tb[DECTA_MAC_INFO_PARI]);
+ if (err < 0)
+ return err;
+ nl_dect_llme_mac_info_set_pari(lmsg, &pari);
+ }
+ if (tb[DECTA_MAC_INFO_RPN] != NULL)
+ nl_dect_llme_mac_info_set_rpn(lmsg, nla_get_u8(tb[DECTA_MAC_INFO_RPN]));
+ if (tb[DECTA_MAC_INFO_RSSI] != NULL)
+ nl_dect_llme_mac_info_set_rssi(lmsg, nla_get_u8(tb[DECTA_MAC_INFO_RSSI]));
+ if (tb[DECTA_MAC_INFO_FPC] != NULL)
+ nl_dect_llme_mac_info_set_fpc(lmsg, nla_get_u32(tb[DECTA_MAC_INFO_FPC]));
+ if (tb[DECTA_MAC_INFO_HLC] != NULL)
+ nl_dect_llme_mac_info_set_hlc(lmsg, nla_get_u16(tb[DECTA_MAC_INFO_HLC]));
+ if (tb[DECTA_MAC_INFO_EFPC] != NULL)
+ nl_dect_llme_mac_info_set_efpc(lmsg, nla_get_u16(tb[DECTA_MAC_INFO_EFPC]));
+ if (tb[DECTA_MAC_INFO_EHLC] != NULL)
+ nl_dect_llme_mac_info_set_ehlc(lmsg, nla_get_u32(tb[DECTA_MAC_INFO_EHLC]));
+ if (tb[DECTA_MAC_INFO_EFPC2] != NULL)
+ nl_dect_llme_mac_info_set_efpc2(lmsg, nla_get_u16(tb[DECTA_MAC_INFO_EFPC2]));
+ if (tb[DECTA_MAC_INFO_EHLC2] != NULL)
+ nl_dect_llme_mac_info_set_ehlc2(lmsg, nla_get_u32(tb[DECTA_MAC_INFO_EHLC2]));
+ return 0;
+}
+
+static int nl_dect_llme_mac_info_build(struct nl_msg *msg,
+ struct nl_dect_llme_msg *lmsg)
+{
+ struct nl_dect_llme_mac_info *mi = mac_info(lmsg);
+ int err;
+
+ if (lmsg->ce_mask & MAC_INFO_ATTR_PARI) {
+ err = nl_dect_fill_ari(msg, &mi->mi_pari, DECTA_MAC_INFO_PARI);
+ if (err < 0)
+ return err;
+ }
+ if (lmsg->ce_mask & MAC_INFO_ATTR_RPN)
+ NLA_PUT_U8(msg, DECTA_MAC_INFO_RPN, mi->mi_rpn);
+ if (lmsg->ce_mask & MAC_INFO_ATTR_FPC)
+ NLA_PUT_U32(msg, DECTA_MAC_INFO_FPC, mi->mi_fpc);
+ if (lmsg->ce_mask & MAC_INFO_ATTR_HLC)
+ NLA_PUT_U16(msg, DECTA_MAC_INFO_HLC, mi->mi_hlc);
+ if (lmsg->ce_mask & MAC_INFO_ATTR_EFPC)
+ NLA_PUT_U16(msg, DECTA_MAC_INFO_EFPC, mi->mi_efpc);
+ if (lmsg->ce_mask & MAC_INFO_ATTR_EHLC)
+ NLA_PUT_U32(msg, DECTA_MAC_INFO_EHLC, mi->mi_ehlc);
+ if (lmsg->ce_mask & MAC_INFO_ATTR_EFPC2)
+ NLA_PUT_U16(msg, DECTA_MAC_INFO_EFPC2, mi->mi_efpc2);
+ if (lmsg->ce_mask & MAC_INFO_ATTR_EHLC2)
+ NLA_PUT_U32(msg, DECTA_MAC_INFO_EHLC2, mi->mi_ehlc2);
+ if (lmsg->ce_mask & MAC_INFO_ATTR_MFN)
+ NLA_PUT_U32(msg, DECTA_MAC_INFO_MFN, mi->mi_mfn);
+ return 0;
+
+nla_put_failure:
+ return -NLE_MSGSIZE;
+}
+
+static const struct nl_dect_llme_link {
+ int (*parse)(struct nl_dect_llme_msg *, struct nlattr *[]);
+ int (*build)(struct nl_msg *, struct nl_dect_llme_msg *);
+ void (*dump)(const struct nl_dect_llme_msg *, struct nl_dump_params *);
+ struct nla_policy *policy;
+ unsigned int maxtype;
+} nl_dect_llme_dispatch[DECT_LLME_MAX + 1] = {
+ [DECT_LLME_SCAN] = {
+ .policy = nl_dect_mac_info_policy,
+ .maxtype = DECTA_MAC_INFO_MAX,
+ .parse = nl_dect_llme_mac_info_parse,
+ .build = nl_dect_llme_mac_info_build,
+ .dump = nl_dect_llme_mac_info_dump,
+ },
+ [DECT_LLME_MAC_INFO] = {
+ .policy = nl_dect_mac_info_policy,
+ .maxtype = DECTA_MAC_INFO_MAX,
+ .parse = nl_dect_llme_mac_info_parse,
+ .build = nl_dect_llme_mac_info_build,
+ .dump = nl_dect_llme_mac_info_dump,
+ },
+ [DECT_LLME_MAC_RFP_PRELOAD] = {
+ .policy = nl_dect_mac_info_policy,
+ .maxtype = DECTA_MAC_INFO_MAX,
+ .parse = nl_dect_llme_mac_info_parse,
+ .build = nl_dect_llme_mac_info_build,
+ .dump = nl_dect_llme_mac_info_dump,
+ },
+};
+
+static void llme_msg_dump(struct nl_object *obj, struct nl_dump_params *p)
+{
+ struct nl_dect_llme_msg *lmsg = nl_object_priv(obj);
+ const struct nl_dect_llme_link *link;
+ char buf1[64], buf2[64];
+
+ nl_dect_llme_msgtype2str(lmsg->lm_type, buf1, sizeof(buf1));
+ nl_dect_llme_op2str(lmsg->lm_op, buf2, sizeof(buf2));
+ nl_dump(p, "%s-%s: ", buf1, buf2);
+
+ link = &nl_dect_llme_dispatch[lmsg->lm_type];
+ link->dump(lmsg, p);
+}
+
+static struct nla_policy nl_dect_llme_policy[DECTA_LLME_MAX + 1] = {
+ [DECTA_LLME_OP] = { .type = NLA_U8 },
+ [DECTA_LLME_TYPE] = { .type = NLA_U8 },
+ [DECTA_LLME_DATA] = { .type = NLA_NESTED },
+};
+
+static int llme_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+ struct nlmsghdr *n, struct nl_parser_param *pp)
+{
+ const struct nl_dect_llme_link *link;
+ struct dectmsg *dm = nlmsg_data(n);
+ struct nlattr *tb[DECTA_LLME_MAX + 1];
+ struct nl_dect_llme_msg *lmsg;
+ uint8_t op, type;
+ int err;
+
+ err = nlmsg_parse(n, sizeof(*dm), tb, DECTA_LLME_MAX, nl_dect_llme_policy);
+ if (err < 0)
+ return err;
+
+ if (tb[DECTA_LLME_OP] == NULL ||
+ tb[DECTA_LLME_TYPE] == NULL ||
+ tb[DECTA_LLME_DATA] == NULL)
+ return -NLE_INVAL;
+
+ type = nla_get_u8(tb[DECTA_LLME_TYPE]);
+ if (type > DECT_LLME_MAX)
+ return -NLE_INVAL;
+ link = &nl_dect_llme_dispatch[type];
+
+ op = nla_get_u8(tb[DECTA_LLME_OP]);
+ switch (op) {
+ case DECT_LLME_REQUEST:
+ case DECT_LLME_INDICATE:
+ case DECT_LLME_RESPONSE:
+ case DECT_LLME_CONFIRM:
+ if (link->parse == NULL)
+ return -NLE_OPNOTSUPP;
+ break;
+ default:
+ return -NLE_INVAL;
+ }
+
+ lmsg = nl_dect_llme_msg_alloc();
+ lmsg->ce_msgtype = n->nlmsg_type;
+ lmsg->lm_index = dm->dm_index;
+
+ nl_dect_llme_msg_set_type(lmsg, type);
+ nl_dect_llme_msg_set_op(lmsg, op);
+
+ if (1) {
+ struct nlattr *nla[link->maxtype + 1];
+
+ err = nla_parse_nested(nla, link->maxtype, tb[DECTA_LLME_DATA],
+ link->policy);
+ if (err < 0)
+ goto errout;
+ err = link->parse(lmsg, nla);
+ if (err < 0)
+ goto errout;
+ }
+
+ err = pp->pp_cb((struct nl_object *)lmsg, pp);
+errout:
+ nl_dect_llme_msg_put(lmsg);
+ return err;
+}
+
+/**
+ * @name message creation
+ * @{
+ */
+
+static int nl_dect_llme_build_msg(struct nl_msg *msg, struct nl_dect_llme_msg *lmsg,
+ enum dect_llme_ops op)
+{
+ const struct nl_dect_llme_link *link;
+ struct nlattr *nest;
+ struct dectmsg dm = {
+ .dm_index = lmsg->lm_index,
+ };
+
+ if (nlmsg_append(msg, &dm, sizeof(dm), NLMSG_ALIGNTO) < 0)
+ goto nla_put_failure;
+ NLA_PUT_U8(msg, DECTA_LLME_OP, op);
+ NLA_PUT_U8(msg, DECTA_LLME_TYPE, lmsg->lm_type);
+
+ link = &nl_dect_llme_dispatch[lmsg->lm_type];
+ nest = nla_nest_start(msg, DECTA_LLME_DATA);
+ if (nest == NULL)
+ goto nla_put_failure;
+ if (link->build(msg, lmsg) < 0)
+ goto nla_put_failure;
+ nla_nest_end(msg, nest);
+
+ return 0;
+
+nla_put_failure:
+ return -NLE_MSGSIZE;
+}
+
+static int build_llme_msg(struct nl_dect_llme_msg *tmpl, enum dect_llme_ops op,
+ struct nl_msg **result)
+{
+ struct nl_msg *msg;
+ int err;
+
+ if (!(msg = nlmsg_alloc_simple(DECT_LLME_MSG, 0)))
+ return -NLE_NOMEM;
+
+ if ((err = nl_dect_llme_build_msg(msg, tmpl, op)) < 0) {
+ nlmsg_free(msg);
+ return err;
+ }
+
+ *result = msg;
+ return 0;
+}
+
+int nl_dect_llme_build_request(struct nl_dect_llme_msg *tmpl,
+ struct nl_msg **result)
+{
+ return build_llme_msg(tmpl, DECT_LLME_REQUEST, result);
+}
+
+int nl_dect_llme_request(struct nl_sock *sk, struct nl_dect_llme_msg *lmsg)
+{
+ struct nl_msg *msg;
+ int err;
+
+ if ((err = nl_dect_llme_build_request(lmsg, &msg)) < 0)
+ return err;
+
+ err = nl_send_auto_complete(sk, msg);
+ nlmsg_free(msg);
+ if (err < 0)
+ return err;
+
+ return wait_for_ack(sk);
+}
+
+int nl_dect_llme_build_response(struct nl_dect_llme_msg *tmpl,
+ struct nl_msg **result)
+{
+ return build_llme_msg(tmpl, DECT_LLME_RESPONSE, result);
+}
+
+int nl_dect_llme_respond(struct nl_sock *sk, struct nl_dect_llme_msg *tmpl)
+{
+ struct nl_msg *msg;
+ int err;
+
+ if ((err = nl_dect_llme_build_response(tmpl, &msg)) < 0)
+ return err;
+
+ err = nl_send_auto_complete(sk, msg);
+ nlmsg_free(msg);
+ if (err < 0)
+ return err;
+
+ return wait_for_ack(sk);
+}
+
+void nl_dect_llme_msg_set_index(struct nl_dect_llme_msg *lmsg, int index)
+{
+ lmsg->lm_index = index;
+}
+
+void nl_dect_llme_msg_set_type(struct nl_dect_llme_msg *lmsg,
+ enum dect_llme_msg_types type)
+{
+ lmsg->lm_type = type;
+}
+
+enum dect_llme_msg_types nl_dect_llme_msg_get_type(const struct nl_dect_llme_msg *lmsg)
+{
+ return lmsg->lm_type;
+}
+
+enum dect_llme_ops nl_dect_llme_msg_get_op(const struct nl_dect_llme_msg *lmsg)
+{
+ return lmsg->lm_op;
+}
+
+void nl_dect_llme_msg_set_op(struct nl_dect_llme_msg *lmsg, enum dect_llme_ops op)
+{
+ lmsg->lm_op = op;
+}
+
+/** @} */
+
+/**
+ * @name Allocation/Freeing
+ * @{
+ */
+
+struct nl_dect_llme_msg *nl_dect_llme_msg_alloc(void)
+{
+ return (struct nl_dect_llme_msg *)nl_object_alloc(&llme_msg_obj_ops);
+}
+
+void nl_dect_llme_msg_get(struct nl_dect_llme_msg *lmsg)
+{
+ nl_object_get((struct nl_object *)lmsg);
+}
+
+void nl_dect_llme_msg_put(struct nl_dect_llme_msg *lmsg)
+{
+ nl_object_put((struct nl_object *)lmsg);
+}
+
+/** @} */
+
+static struct trans_tbl llme_types[] = {
+ __ADD(DECT_LLME_SCAN, SCAN)
+ __ADD(DECT_LLME_MAC_INFO, MAC_INFO)
+};
+
+char *nl_dect_llme_msgtype2str(enum dect_llme_msg_types type, char *buf, size_t len)
+{
+ return __type2str(type, buf, len, llme_types, ARRAY_SIZE(llme_types));
+}
+
+enum dect_llme_msg_types nl_dect_llme_str2msgtype(const char *str)
+{
+ return __str2type(str, llme_types, ARRAY_SIZE(llme_types));
+}
+
+static struct trans_tbl llme_ops[] = {
+ __ADD(DECT_LLME_REQUEST, req)
+ __ADD(DECT_LLME_INDICATE, ind)
+ __ADD(DECT_LLME_RESPONSE, res)
+ __ADD(DECT_LLME_CONFIRM, cfm)
+};
+
+char *nl_dect_llme_op2str(enum dect_llme_ops op, char *buf, size_t len)
+{
+ return __type2str(op, buf, len, llme_ops, ARRAY_SIZE(llme_ops));
+}
+
+enum dect_llme_ops nl_dect_llme_str2op(const char *str)
+{
+ return __str2type(str, llme_ops, ARRAY_SIZE(llme_ops));
+}
+
+/** @cond SKIP */
+static struct nl_object_ops llme_msg_obj_ops = {
+ .oo_name = "nl_dect/llme_msg",
+ .oo_size = sizeof(struct nl_dect_llme_msg),
+ .oo_dump = {
+ [NL_DUMP_LINE] = llme_msg_dump,
+ },
+};
+
+static struct nl_cache_ops nl_dect_llme_msg_ops = {
+ .co_name = "nl_dect/llme_msg",
+ .co_protocol = NETLINK_DECT,
+ .co_msgtypes = {
+ { DECT_LLME_MSG, NL_ACT_NEW, "new" },
+ END_OF_MSGTYPES_LIST,
+ },
+ .co_msg_parser = llme_msg_parser,
+ .co_obj_ops = &llme_msg_obj_ops,
+};
+/** @endcond */
+
+static void __init llme_init(void)
+{
+ nl_cache_mngt_register(&nl_dect_llme_msg_ops);
+}
+
+static void __exit llme_exit(void)
+{
+ nl_cache_mngt_unregister(&nl_dect_llme_msg_ops);
+}
+
+/** @} */
diff --git a/lib/dect/transceiver.c b/lib/dect/transceiver.c
new file mode 100644
index 0000000..330fbd4
--- /dev/null
+++ b/lib/dect/transceiver.c
@@ -0,0 +1,307 @@
+/*
+ * lib/dect/transceiver.c DECT Transceiver objects
+ *
+ * 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 version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
+ */
+
+/**
+ * @ingroup dect
+ * @defgroup dect Transceivers
+ * @brief
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/dect/transceiver.h>
+
+/** @cond SKIP */
+static struct nl_cache_ops nl_dect_transceiver_ops;
+/** @endcond */
+
+#define DECTNAMSIZ 16
+
+static struct nla_policy transceiver_policy[DECTA_TRANSCEIVER_MAX + 1] = {
+ [DECTA_TRANSCEIVER_NAME] = { .type = NLA_STRING, .maxlen = DECTNAMSIZ },
+ [DECTA_TRANSCEIVER_TYPE] = { .type = NLA_STRING },
+ [DECTA_TRANSCEIVER_FEATURES] = { .type = NLA_U32 },
+ [DECTA_TRANSCEIVER_LINK] = { .type = NLA_U8 },
+ [DECTA_TRANSCEIVER_STATS] = { .type = NLA_NESTED },
+ [DECTA_TRANSCEIVER_BAND] = { .type = NLA_U8 },
+ [DECTA_TRANSCEIVER_SLOTS] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy stats_policy[DECTA_TRANSCEIVER_STATS_MAX + 1] = {
+ [DECTA_TRANSCEIVER_STATS_EVENT_BUSY] = { .type = NLA_U32 },
+ [DECTA_TRANSCEIVER_STATS_EVENT_LATE] = { .type = NLA_U32 },
+};
+
+static struct nla_policy slot_policy[DECTA_SLOT_MAX + 1] = {
+ [DECTA_SLOT_NUM] = { .type = NLA_U8 },
+ [DECTA_SLOT_STATE] = { .type = NLA_U8 },
+ [DECTA_SLOT_FLAGS] = { .type = NLA_U32 },
+ [DECTA_SLOT_PACKET] = { .type = NLA_U8 },
+ [DECTA_SLOT_CARRIER] = { .type = NLA_U8 },
+ [DECTA_SLOT_FREQUENCY] = { .type = NLA_U32 },
+ [DECTA_SLOT_PHASEOFF] = { .type = NLA_U32 },
+ [DECTA_SLOT_RSSI] = { .type = NLA_U8 },
+ [DECTA_SLOT_RX_BYTES] = { .type = NLA_U32 },
+ [DECTA_SLOT_RX_PACKETS] = { .type = NLA_U32 },
+ [DECTA_SLOT_RX_A_CRC_ERRORS] = { .type = NLA_U32 },
+ [DECTA_SLOT_RX_X_CRC_ERRORS] = { .type = NLA_U32 },
+ [DECTA_SLOT_RX_Z_CRC_ERRORS] = { .type = NLA_U32 },
+ [DECTA_SLOT_TX_BYTES] = { .type = NLA_U32 },
+ [DECTA_SLOT_TX_PACKETS] = { .type = NLA_U32 },
+};
+
+static int slot_parser(struct nl_dect_transceiver *trx, struct nlattr *nla)
+{
+ struct nlattr *tb[DECTA_SLOT_MAX + 1];
+ struct nl_dect_transceiver_slot *dts;
+ uint8_t slot;
+ int err;
+
+ err = nla_parse_nested(tb, DECTA_SLOT_MAX, nla, slot_policy);
+ if (err < 0)
+ return err;
+
+ if (tb[DECTA_SLOT_NUM] == NULL)
+ return -NLE_INVAL;
+ slot = nla_get_u8(tb[DECTA_SLOT_NUM]);
+ dts = &trx->trx_slots[slot];
+
+ dts->dts_valid = 1;
+ if (tb[DECTA_SLOT_STATE] != NULL)
+ dts->dts_state = nla_get_u8(tb[DECTA_SLOT_STATE]);
+ if (tb[DECTA_SLOT_FLAGS] != NULL)
+ dts->dts_flags = nla_get_u32(tb[DECTA_SLOT_FLAGS]);
+ if (tb[DECTA_SLOT_PACKET] != NULL)
+ dts->dts_packet = nla_get_u8(tb[DECTA_SLOT_PACKET]);
+ if (tb[DECTA_SLOT_CARRIER] != NULL)
+ dts->dts_carrier = nla_get_u8(tb[DECTA_SLOT_CARRIER]);
+ if (tb[DECTA_SLOT_FREQUENCY] != NULL)
+ dts->dts_frequency = nla_get_u32(tb[DECTA_SLOT_FREQUENCY]);
+ if (tb[DECTA_SLOT_PHASEOFF] != NULL)
+ dts->dts_phaseoff = nla_get_u32(tb[DECTA_SLOT_PHASEOFF]);
+ if (tb[DECTA_SLOT_RSSI] != NULL)
+ dts->dts_rssi = nla_get_u8(tb[DECTA_SLOT_RSSI]);
+ if (tb[DECTA_SLOT_RX_BYTES] != NULL)
+ dts->dts_rx_bytes = nla_get_u32(tb[DECTA_SLOT_RX_BYTES]);
+ if (tb[DECTA_SLOT_RX_PACKETS] != NULL)
+ dts->dts_rx_packets = nla_get_u32(tb[DECTA_SLOT_RX_PACKETS]);
+ if (tb[DECTA_SLOT_RX_A_CRC_ERRORS] != NULL)
+ dts->dts_rx_a_crc_errors = nla_get_u32(tb[DECTA_SLOT_RX_A_CRC_ERRORS]);
+ if (tb[DECTA_SLOT_RX_X_CRC_ERRORS] != NULL)
+ dts->dts_rx_x_crc_errors = nla_get_u32(tb[DECTA_SLOT_RX_X_CRC_ERRORS]);
+ if (tb[DECTA_SLOT_RX_Z_CRC_ERRORS] != NULL)
+ dts->dts_rx_z_crc_errors = nla_get_u32(tb[DECTA_SLOT_RX_Z_CRC_ERRORS]);
+ if (tb[DECTA_SLOT_TX_BYTES] != NULL)
+ dts->dts_tx_bytes = nla_get_u32(tb[DECTA_SLOT_TX_BYTES]);
+ if (tb[DECTA_SLOT_TX_PACKETS] != NULL)
+ dts->dts_tx_packets = nla_get_u32(tb[DECTA_SLOT_TX_PACKETS]);
+ return 0;
+}
+
+static int stats_parser(struct nl_dect_transceiver *trx, struct nlattr *nla)
+{
+ struct nlattr *tb[DECTA_TRANSCEIVER_STATS_MAX + 1];
+ struct nl_dect_transceiver_stats *stats = &trx->trx_stats;
+ int err;
+
+ err = nla_parse_nested(tb, DECTA_TRANSCEIVER_STATS_MAX, nla, stats_policy);
+ if (err < 0)
+ return err;
+
+ if (tb[DECTA_TRANSCEIVER_STATS_EVENT_BUSY] != NULL)
+ stats->trx_event_busy =
+ nla_get_u32(tb[DECTA_TRANSCEIVER_STATS_EVENT_BUSY]);
+ if (tb[DECTA_TRANSCEIVER_STATS_EVENT_LATE] != NULL)
+ stats->trx_event_late =
+ nla_get_u32(tb[DECTA_TRANSCEIVER_STATS_EVENT_LATE]);
+ return 0;
+}
+
+static int transceiver_msg_parser(struct nl_cache_ops *ops,
+ struct sockaddr_nl *who,
+ struct nlmsghdr *n,
+ struct nl_parser_param *pp)
+{
+ struct nlattr *tb[DECTA_TRANSCEIVER_MAX + 1], *nla;
+ struct nl_dect_transceiver *trx;
+ int err;
+
+ trx = nl_dect_transceiver_alloc();
+ if (trx == NULL) {
+ err = -NLE_NOMEM;
+ goto errout;
+ }
+
+ trx->ce_msgtype = n->nlmsg_type;
+
+ err = nlmsg_parse(n, sizeof(struct dectmsg), tb, DECTA_TRANSCEIVER_MAX,
+ transceiver_policy);
+ if (err < 0)
+ goto errout;
+
+ if (tb[DECTA_TRANSCEIVER_NAME] != NULL) {
+ char name[DECTNAMSIZ];
+ nla_strlcpy(name, tb[DECTA_TRANSCEIVER_NAME], sizeof(name));
+ nl_dect_transceiver_set_name(trx, name);
+ }
+
+ if (tb[DECTA_TRANSCEIVER_TYPE] != NULL) {
+ char *type = nla_strdup(tb[DECTA_TRANSCEIVER_TYPE]);
+ if (type == NULL) {
+ err = -NLE_NOMEM;
+ goto errout;
+ }
+ nl_dect_transceiver_set_type(trx, type);
+ free(type);
+ }
+
+ if (tb[DECTA_TRANSCEIVER_FEATURES] != NULL)
+ nl_dect_transceiver_set_features(trx, nla_get_u32(tb[DECTA_TRANSCEIVER_FEATURES]));
+
+ if (tb[DECTA_TRANSCEIVER_LINK] != NULL)
+ nl_dect_transceiver_set_link(trx, nla_get_u8(tb[DECTA_TRANSCEIVER_LINK]));
+
+ if (tb[DECTA_TRANSCEIVER_STATS] != NULL) {
+ err = stats_parser(trx, tb[DECTA_TRANSCEIVER_STATS]);
+ if (err < 0)
+ goto errout;
+ }
+
+ if (tb[DECTA_TRANSCEIVER_BAND] != NULL)
+ nl_dect_transceiver_set_band(trx, nla_get_u8(tb[DECTA_TRANSCEIVER_BAND]));
+
+ if (tb[DECTA_TRANSCEIVER_SLOTS] != NULL) {
+ int rem;
+ nla_for_each_nested(nla, tb[DECTA_TRANSCEIVER_SLOTS], rem) {
+ if (nla_type(nla) != DECTA_LIST_ELEM)
+ continue;
+ err = slot_parser(trx, nla);
+ if (err < 0)
+ goto errout;
+ }
+ }
+
+ err = pp->pp_cb((struct nl_object *)trx, pp);
+errout:
+ nl_dect_transceiver_put(trx);
+ return err;
+}
+
+static int transceiver_request_update(struct nl_cache *c, struct nl_sock *h)
+{
+ struct dectmsg dm;
+
+ memset(&dm, 0, sizeof(dm));
+ return nl_send_simple(h, DECT_GET_TRANSCEIVER, NLM_F_DUMP,
+ &dm, sizeof(dm));
+}
+
+/**
+ * @name Cache Management
+ * @{
+ */
+int nl_dect_transceiver_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
+{
+ struct nl_cache *cache;
+ int err;
+
+ cache = nl_cache_alloc(&nl_dect_transceiver_ops);
+ if (cache == NULL)
+ return -NLE_NOMEM;
+
+ if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
+ free(cache);
+ return err;
+ }
+
+ *result = cache;
+ return 0;
+}
+
+/** @} */
+
+/**
+ * @name Transceiver creation
+ * @{
+ */
+
+static int build_transceiver_msg(struct nl_dect_transceiver *tmpl, int cmd,
+ int flags, struct nl_msg **result)
+{
+ struct nl_msg *msg;
+ int err;
+
+ msg = nlmsg_alloc_simple(cmd, flags);
+ if (msg == NULL)
+ return -NLE_NOMEM;
+
+ err = nl_dect_transceiver_build_msg(msg, tmpl);
+ if (err < 0) {
+ nlmsg_free(msg);
+ return err;
+ }
+
+ *result = msg;
+ return 0;
+}
+
+int nl_dect_transceiver_build_change_request(struct nl_dect_transceiver *tmpl,
+ int flags, struct nl_msg **result)
+{
+ return build_transceiver_msg(tmpl, DECT_NEW_TRANSCEIVER, flags, result);
+}
+
+int nl_dect_transceiver_change(struct nl_sock *sk, struct nl_dect_transceiver *trx,
+ int flags)
+{
+ struct nl_msg *msg;
+ int err;
+
+ err = nl_dect_transceiver_build_change_request(trx, flags, &msg);
+ if (err < 0)
+ return err;
+
+ err = nl_send_auto_complete(sk, msg);
+ nlmsg_free(msg);
+ if (err < 0)
+ return err;
+
+ return wait_for_ack(sk);
+}
+
+static struct nl_cache_ops nl_dect_transceiver_ops = {
+ .co_name = "nl_dect/transceiver",
+ .co_hdrsize = 0,
+ .co_msgtypes = {
+ { DECT_NEW_TRANSCEIVER, NL_ACT_NEW, "new" },
+ { DECT_GET_TRANSCEIVER, NL_ACT_GET, "get" },
+ { DECT_DEL_TRANSCEIVER, NL_ACT_DEL, "del" },
+ END_OF_MSGTYPES_LIST
+ },
+ .co_protocol = NETLINK_DECT,
+ .co_request_update = transceiver_request_update,
+ .co_msg_parser = transceiver_msg_parser,
+ .co_obj_ops = &nl_dect_transceiver_obj_ops,
+};
+
+static void __init transceiver_init(void)
+{
+ nl_cache_mngt_register(&nl_dect_transceiver_ops);
+}
+
+static void __exit transceiver_exit(void)
+{
+ nl_cache_mngt_unregister(&nl_dect_transceiver_ops);
+}
+
+/** @} */
diff --git a/lib/dect/transceiver_obj.c b/lib/dect/transceiver_obj.c
new file mode 100644
index 0000000..1638471
--- /dev/null
+++ b/lib/dect/transceiver_obj.c
@@ -0,0 +1,335 @@
+/*
+ * lib/dect/transceiver_obj.c DECT Transceiver objects
+ *
+ * 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 version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/utils.h>
+#include <netlink/data.h>
+#include <netlink/dect/dect.h>
+#include <netlink/dect/cell.h>
+#include <netlink/dect/transceiver.h>
+
+/** @cond SKIP */
+#define TRANSCEIVER_ATTR_NAME 0x0001
+#define TRANSCEIVER_ATTR_TYPE 0x0002
+#define TRANSCEIVER_ATTR_FEATURES 0x0004
+#define TRANSCEIVER_ATTR_INDEX 0x0008
+#define TRANSCEIVER_ATTR_LINK 0x0010
+#define TRANSCEIVER_ATTR_BAND 0x0020
+/** @endtsond */
+
+static void transceiver_free_data(struct nl_object *obj)
+{
+ struct nl_dect_transceiver *trx = nl_object_priv(obj);
+
+ if (trx == NULL)
+ return;
+ free(trx->trx_name);
+ free(trx->trx_type);
+}
+
+static void slot_dump(struct nl_dect_transceiver_slot *dts, unsigned int n,
+ struct nl_dump_params *p)
+{
+ int64_t offset;
+ char buf[64];
+
+ nl_dect_slot_state2str(dts->dts_state, buf, sizeof(buf));
+ nl_dump(p, "\tslot %u: <%s", n, buf);
+ if (dts->dts_flags) {
+ nl_dect_slot_flags2str(dts->dts_flags, buf, sizeof(buf));
+ nl_dump(p, ",%s", buf);
+ }
+ nl_dump(p, "> ");
+
+ if (dts->dts_state != DECT_SLOT_IDLE) {
+ nl_dump(p, "packet: %s ",
+ nl_dect_slot_packet2str(dts->dts_packet, buf,
+ sizeof(buf)));
+
+ nl_dump(p, "carrier: %u (%u.%03u MHz", dts->dts_carrier,
+ dts->dts_frequency / 1000, dts->dts_frequency % 1000);
+
+ if (dts->dts_state == DECT_SLOT_RX) {
+ offset = (int64_t)dts->dts_frequency *
+ dts->dts_phaseoff /
+ DECT_PHASE_OFFSET_SCALE;
+ nl_dump(p, " %+" PRId64 ".%03" PRIu64 " kHz",
+ offset / 1000000,
+ llabs(offset) % 1000000 / 1000);
+ }
+ nl_dump(p, ")");
+ }
+
+ if (dts->dts_state == DECT_SLOT_RX)
+ nl_dump(p, " signal level: %.2fdBm",
+ nl_dect_rssi_to_dbm(dts->dts_rssi));
+ nl_dump(p, "\n");
+
+ nl_dump(p, "\t RX: bytes %u packets %u a-crc-errors %u "
+ "x-crc-errors %u z-crc-errors %u\n",
+ dts->dts_rx_bytes, dts->dts_rx_packets,
+ dts->dts_rx_a_crc_errors,
+ dts->dts_rx_x_crc_errors,
+ dts->dts_rx_z_crc_errors);
+ nl_dump(p, "\t TX: bytes %u packets %u\n",
+ dts->dts_tx_bytes, dts->dts_tx_packets);
+}
+
+static void transceiver_dump(struct nl_object *obj, struct nl_dump_params *p)
+{
+ struct nl_dect_transceiver *trx = nl_object_priv(obj);
+ struct nl_dect_transceiver_stats *stats = &trx->trx_stats;
+ struct nl_dect_transceiver_slot *dts;
+ struct nl_cache *cell_cache;
+ char buf[64];
+ unsigned int n;
+
+ nl_dump(p, "DECT Transceiver ");
+ if (trx->trx_name != NULL)
+ nl_dump_line(p, "%s", trx->trx_name);
+
+ if (trx->trx_link) {
+ cell_cache = nl_cache_mngt_require("nl_dect/cell");
+ if (cell_cache != NULL) {
+ nl_dump(p, "@%s",
+ nl_dect_cell_i2name(cell_cache, trx->trx_link,
+ buf, sizeof(buf)));
+ } else
+ nl_dump(p, "@%d", trx->trx_link);
+ }
+ nl_dump(p, ":\n");
+ if (trx->trx_type != NULL)
+ nl_dump_line(p, "\tType: %s\n", trx->trx_type);
+ if (trx->trx_features != 0)
+ nl_dump_line(p, "\tFeatures: %s\n",
+ nl_dect_transceiver_features2str(trx->trx_features,
+ buf, sizeof(buf)));
+ nl_dump(p, "\tRF-band: %.5u\n", trx->trx_band);
+ nl_dump(p, "\tEvents: busy: %u late: %u\n",
+ stats->trx_event_busy, stats->trx_event_late);
+
+ nl_dump(p, "\n");
+ for (n = 0; n < 24; n++) {
+ dts = &trx->trx_slots[n];
+ if (!dts->dts_valid)
+ continue;
+ slot_dump(dts, n, p);
+ }
+}
+
+static int nl_dect_transceiver_compare(struct nl_object *_a, struct nl_object *_b,
+ uint32_t attrs, int flags)
+{
+ struct nl_dect_transceiver *a = (struct nl_dect_transceiver *)_a;
+ struct nl_dect_transceiver *b = (struct nl_dect_transceiver *)_b;
+ int diff = 0;
+
+#define TRX_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, TRANSCEIVER_ATTR_##ATTR, a, b, EXPR)
+
+ diff |= TRX_DIFF(NAME, strcmp(a->trx_name, b->trx_name));
+ diff |= TRX_DIFF(LINK, a->trx_link != b->trx_link);
+
+#undef TRX_DIFF
+
+ return diff;
+}
+
+/**
+ * @name Allocation/Freeing
+ * @{
+ */
+
+struct nl_dect_transceiver *nl_dect_transceiver_alloc(void)
+{
+ return (struct nl_dect_transceiver *)nl_object_alloc(&nl_dect_transceiver_obj_ops);
+}
+
+void nl_dect_transceiver_get(struct nl_dect_transceiver *trx)
+{
+ nl_object_get((struct nl_object *)trx);
+}
+
+void nl_dect_transceiver_put(struct nl_dect_transceiver *trx)
+{
+ nl_object_put((struct nl_object *)trx);
+}
+
+/** @} */
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+void nl_dect_transceiver_set_name(struct nl_dect_transceiver *trx, const char *name)
+{
+ trx->trx_name = strdup(name);
+ trx->ce_mask |= TRANSCEIVER_ATTR_NAME;
+}
+
+bool nl_dect_transceiver_test_name(const struct nl_dect_transceiver *trx)
+{
+ return !!(trx->ce_mask & TRANSCEIVER_ATTR_NAME);
+}
+
+const char *nl_dect_transceiver_get_name(const struct nl_dect_transceiver *trx)
+{
+ return trx->trx_name;
+}
+
+void nl_dect_transceiver_set_type(struct nl_dect_transceiver *trx, const char *type)
+{
+ trx->trx_type = strdup(type);
+ trx->ce_mask |= TRANSCEIVER_ATTR_TYPE;
+}
+
+bool nl_dect_transceiver_test_type(const struct nl_dect_transceiver *trx)
+{
+ return !!(trx->ce_mask & TRANSCEIVER_ATTR_TYPE);
+}
+
+const char *nl_dect_transceiver_get_type(const struct nl_dect_transceiver *trx)
+{
+ return trx->trx_type;
+}
+
+void nl_dect_transceiver_set_features(struct nl_dect_transceiver *trx, uint32_t features)
+{
+ trx->trx_features = features;
+ trx->ce_mask |= TRANSCEIVER_ATTR_FEATURES;
+}
+
+void nl_dect_transceiver_set_index(struct nl_dect_transceiver *trx, int index)
+{
+ trx->trx_index = index;
+ trx->ce_mask |= TRANSCEIVER_ATTR_INDEX;
+}
+
+void nl_dect_transceiver_set_link(struct nl_dect_transceiver *trx, uint8_t link)
+{
+ trx->trx_link = link;
+ trx->ce_mask |= TRANSCEIVER_ATTR_LINK;
+}
+
+void nl_dect_transceiver_set_band(struct nl_dect_transceiver *trx, uint8_t band)
+{
+ trx->trx_band = band;
+ trx->ce_mask |= TRANSCEIVER_ATTR_BAND;
+}
+
+bool nl_dect_transceiver_test_band(const struct nl_dect_transceiver *trx)
+{
+ return !!(trx->ce_mask & TRANSCEIVER_ATTR_BAND);
+}
+
+uint8_t nl_dect_transceiver_get_band(const struct nl_dect_transceiver *trx)
+{
+ return trx->trx_band;
+}
+
+static struct trans_tbl trx_features[] = {
+ __ADD(DECT_TRANSCEIVER_SLOW_HOPPING, slow-hopping)
+ __ADD(DECT_TRANSCEIVER_PACKET_P64, p64)
+};
+
+const char *nl_dect_transceiver_features2str(uint32_t features, char *buf, size_t len)
+{
+ return __flags2str(features, buf, len, trx_features,
+ ARRAY_SIZE(trx_features));
+}
+
+static struct trans_tbl slot_states[] = {
+ __ADD(DECT_SLOT_IDLE, idle)
+ __ADD(DECT_SLOT_SCANNING, scanning)
+ __ADD(DECT_SLOT_RX, rx)
+ __ADD(DECT_SLOT_TX, tx)
+};
+
+const char *nl_dect_slot_state2str(uint8_t state, char *buf, size_t len)
+{
+ return __type2str(state, buf, len, slot_states,
+ ARRAY_SIZE(slot_states));
+}
+
+uint8_t nl_dect_slot_str2state(const char *str)
+{
+ return __str2type(str, slot_states, ARRAY_SIZE(slot_states));
+}
+
+static struct trans_tbl slot_flags[] = {
+ __ADD(DECT_SLOT_SYNC, sync)
+ __ADD(DECT_SLOT_CIPHER, cipher)
+};
+
+const char *nl_dect_slot_flags2str(uint32_t state, char *buf, size_t len)
+{
+ return __flags2str(state, buf, len, slot_flags, ARRAY_SIZE(slot_flags));
+}
+
+uint32_t nl_dect_slot_str2flags(const char *str)
+{
+ return __str2flags(str, slot_flags, ARRAY_SIZE(slot_flags));
+}
+
+static struct trans_tbl packet_types[] = {
+ __ADD(DECT_PACKET_P00, P00)
+ __ADD(DECT_PACKET_P08, P08)
+ __ADD(DECT_PACKET_P32, P32)
+ __ADD(DECT_PACKET_P80, P80)
+ __ADD(DECT_PACKET_P640j, P640j)
+ __ADD(DECT_PACKET_P640j, P672j)
+};
+
+const char *nl_dect_slot_packet2str(uint8_t pkt, char *buf, size_t len)
+{
+ return __type2str(pkt, buf, len, packet_types, ARRAY_SIZE(packet_types));
+}
+
+uint8_t nl_dect_slot_str2packet(const char *str)
+{
+ return __str2type(str, packet_types, ARRAY_SIZE(packet_types));
+}
+
+int nl_dect_transceiver_build_msg(struct nl_msg *msg, struct nl_dect_transceiver *trx)
+{
+ struct dectmsg dm = {
+ .dm_index = trx->trx_index,
+ };
+
+ if (nlmsg_append(msg, &dm, sizeof(dm), NLMSG_ALIGNTO) < 0)
+ goto nla_put_failure;
+ if (trx->ce_mask & TRANSCEIVER_ATTR_NAME)
+ NLA_PUT_STRING(msg, DECTA_TRANSCEIVER_NAME, trx->trx_name);
+ if (trx->ce_mask & TRANSCEIVER_ATTR_LINK)
+ NLA_PUT_U8(msg, DECTA_TRANSCEIVER_LINK, trx->trx_link);
+ return 0;
+
+nla_put_failure:
+ return -NLE_MSGSIZE;
+}
+
+/** @cond SKIP */
+struct nl_object_ops nl_dect_transceiver_obj_ops = {
+ .oo_name = "nl_dect/transceiver",
+ .oo_size = sizeof(struct nl_dect_transceiver),
+ .oo_free_data = transceiver_free_data,
+ .oo_dump = {
+ [NL_DUMP_LINE] = transceiver_dump,
+ },
+ .oo_compare = nl_dect_transceiver_compare,
+ .oo_id_attrs = TRANSCEIVER_ATTR_NAME,
+};
+
+/** @endcond */
+
+/** @} */
diff --git a/libnl-dect-3.0.pc.in b/libnl-dect-3.0.pc.in
new file mode 100644
index 0000000..5bab3bf
--- /dev/null
+++ b/libnl-dect-3.0.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libnl-dect
+Description: DECT Netlink Library
+Version: @PACKAGE_VERSION@
+Requires: libnl-3.0
+Libs: -L${libdir} -lnl-dect-@MAJ_VERSION@
+Cflags: -I${includedir}/libnl@MAJ_VERSION@
diff --git a/src/.gitignore b/src/.gitignore
index 3e091cb..1624901 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -38,6 +38,15 @@ nl-rule-list
nl-tctree-list
nl-util-addr
nf-queue
+dect-transceiver-list
+dect-transceiver-bind
+dect-cell-add
+dect-cell-delete
+dect-cell-list
+dect-cluster-add
+dect-cluster-delete
+dect-cluster-list
+dect-llme-scan
nl-classid-lookup
nl-pktloc-lookup
nl-link-enslave
diff --git a/src/Makefile.am b/src/Makefile.am
index c318dcc..aa6ffe7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -9,7 +9,8 @@ LDADD = \
${top_builddir}/lib/libnl-3.la \
${top_builddir}/lib/libnl-nf-3.la \
${top_builddir}/lib/libnl-genl-3.la \
- ${top_builddir}/lib/libnl-route-3.la
+ ${top_builddir}/lib/libnl-route-3.la \
+ $(top_builddir)/lib/libnl-dect-3.la
sbin_PROGRAMS = \
genl-ctrl-list \
@@ -36,7 +37,11 @@ noinst_PROGRAMS = \
nl-list-caches nl-list-sockets \
nl-util-addr \
nl-link-enslave \
- nl-link-release
+ nl-link-release \
+ dect-transceiver-bind dect-transceiver-list \
+ dect-cell-add dect-cell-delete dect-cell-list \
+ dect-cluster-add dect-cluster-delete dect-cluster-list \
+ dect-llme-monitor dect-llme-scan
genl_ctrl_list_SOURCES = genl-ctrl-list.c
@@ -96,5 +101,20 @@ nl_list_sockets_SOURCES = nl-list-sockets.c
nl_util_addr_SOURCES = nl-util-addr.c
nl_pktloc_lookup_SOURCES = nl-pktloc-lookup.c
+nl_pktloc_lookup_LDADD = -lnl-route
nl_classid_lookup_SOURCES = nl-classid-lookup.c
+
+dect_transceiver_bind_SOURCES = dect-transceiver-bind.c
+dect_transceiver_list_SOURCES = dect-transceiver-list.c
+
+dect_cell_add_SOURCES = dect-cell-add.c
+dect_cell_delete_SOURCES = dect-cell-delete.c
+dect_cell_list_SOURCES = dect-cell-list.c
+
+dect_cluster_add_SOURCES = dect-cluster-add.c
+dect_cluster_delete_SOURCES = dect-cluster-delete.c
+dect_cluster_list_SOURCES = dect-cluster-list.c
+
+dect_llme_monitor_SOURCES = dect-llme-monitor.c
+dect_llme_scan_SOURCES = dect-llme-scan.c
diff --git a/src/dect-cell-add.c b/src/dect-cell-add.c
new file mode 100644
index 0000000..f9448a0
--- /dev/null
+++ b/src/dect-cell-add.c
@@ -0,0 +1,67 @@
+#include "netlink/cli/utils.h"
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct nl_dect_cell *cell;
+ struct nl_cache *cluster_cache;
+ struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_fd = stdout,
+ };
+ uint8_t cli;
+ int err;
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_DECT);
+
+ if (nl_dect_cluster_alloc_cache(sock, &cluster_cache))
+ exit(1);
+ nl_cache_mngt_provide(cluster_cache);
+
+ cell = nl_dect_cell_alloc();
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_NAME = 256,
+ ARG_FLAGS,
+ ARG_CLUSTER,
+ };
+ static struct option long_opts[] = {
+ { "name", 1, 0, ARG_NAME },
+ { "flags", 1, 0, ARG_FLAGS },
+ { "cluster", 1, 0, ARG_CLUSTER },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "qhvd:n:t:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'v': nl_cli_print_version(); break;
+ case ARG_NAME:
+ nl_dect_cell_set_name(cell, optarg);
+ break;
+ case ARG_FLAGS:
+ nl_dect_cell_set_flags(cell, nl_dect_cell_str2flags(optarg));
+ break;
+ case ARG_CLUSTER:
+ if (isdigit(*optarg))
+ cli = strtoul(optarg, NULL, 0);
+ else
+ cli = nl_dect_cluster_name2i(cluster_cache, optarg);
+ nl_dect_cell_set_link(cell, cli);
+ break;
+ }
+ }
+
+ err = nl_dect_cell_add(sock, cell, 0);
+ if (err < 0)
+ nl_cli_fatal(err, "Unable to add cell: %s", nl_geterror(err));
+
+ printf("Added: ");
+ nl_object_dump(OBJ_CAST(cell), &params);
+ return 0;
+}
+
diff --git a/src/dect-cell-delete.c b/src/dect-cell-delete.c
new file mode 100644
index 0000000..2cb2747
--- /dev/null
+++ b/src/dect-cell-delete.c
@@ -0,0 +1,59 @@
+#include "netlink/cli/utils.h"
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct nl_dect_cell *cell;
+ struct nl_cache *cluster_cache;
+ struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_fd = stdout,
+ };
+ int err;
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_DECT);
+
+ if (nl_dect_cluster_alloc_cache(sock, &cluster_cache))
+ exit(1);
+ nl_cache_mngt_provide(cluster_cache);
+
+ cell = nl_dect_cell_alloc();
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_CELL = 257,
+ ARG_CLUSTER,
+ ARG_TRANSCEIVER,
+ };
+ static struct option long_opts[] = {
+ { "cell", 1, 0, ARG_CELL },
+ { "cluster", 1, 0, ARG_CLUSTER },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "qhvd:n:t:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'v': nl_cli_print_version(); break;
+ case ARG_CELL:
+ nl_dect_cell_set_name(cell, optarg);
+ break;
+ case ARG_CLUSTER:
+ nl_dect_cell_set_link(cell,
+ nl_dect_cluster_name2i(cluster_cache, optarg));
+ break;
+ }
+ }
+
+ err = nl_dect_cell_delete(sock, cell, 0);
+ if (err < 0)
+ nl_cli_fatal(err, "Unable to delete cell: %s", nl_geterror(err));
+
+ printf("Deleted: ");
+ nl_object_dump(OBJ_CAST(cell), &params);
+ return 0;
+}
+
diff --git a/src/dect-cell-list.c b/src/dect-cell-list.c
new file mode 100644
index 0000000..bb946ed
--- /dev/null
+++ b/src/dect-cell-list.c
@@ -0,0 +1,24 @@
+#include "netlink/cli/utils.h"
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct nl_cache *cell_cache;
+ struct nl_cache *cluster_cache;
+ struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_fd = stdout,
+ };
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_DECT);
+ if (nl_dect_cluster_alloc_cache(sock, &cluster_cache))
+ exit(1);
+ nl_cache_mngt_provide(cluster_cache);
+ if (nl_dect_cell_alloc_cache(sock, &cell_cache))
+ exit(1);
+
+ nl_cache_dump(cell_cache, &params);
+ return 0;
+}
+
diff --git a/src/dect-cluster-add.c b/src/dect-cluster-add.c
new file mode 100644
index 0000000..12db751
--- /dev/null
+++ b/src/dect-cluster-add.c
@@ -0,0 +1,95 @@
+#include "netlink/cli/utils.h"
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct nl_dect_cluster *cl;
+ struct nl_dect_ari *pari;
+ struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_fd = stdout,
+ };
+ int err;
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_DECT);
+ cl = nl_dect_cluster_alloc();
+ pari = (void *)nl_dect_cluster_get_pari(cl);
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_NAME = 257,
+ ARG_MODE,
+ ARG_CLASS,
+ ARG_EMC,
+ ARG_EIC,
+ ARG_POC,
+ ARG_GOP,
+ ARG_FIL,
+ ARG_FPS,
+ ARG_FPN,
+ };
+ static struct option long_opts[] = {
+ { "name", 1, 0, ARG_NAME },
+ { "mode", 1, 0, ARG_MODE },
+ { "class", 1, 0, ARG_CLASS },
+ { "emc", 1, 0, ARG_EMC },
+ { "eic", 1, 0, ARG_EIC },
+ { "poc", 1, 0, ARG_POC },
+ { "gop", 1, 0, ARG_GOP },
+ { "fil", 1, 0, ARG_FIL },
+ { "fps", 1, 0, ARG_FPS },
+ { "fpn", 1, 0, ARG_FPN },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "qhvd:n:t:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'v': nl_cli_print_version(); break;
+ case ARG_NAME:
+ nl_dect_cluster_set_name(cl, strdup(optarg));
+ break;
+ case ARG_MODE:
+ nl_dect_cluster_set_mode(cl, nl_dect_cluster_str2mode(optarg));
+ break;
+ case ARG_CLASS:
+ nl_dect_ari_set_class(pari, nl_dect_ari_str2class(optarg));
+ break;
+ case ARG_EMC:
+ nl_dect_ari_set_emc(pari, strtoul(optarg, NULL, 16));
+ break;
+ case ARG_EIC:
+ nl_dect_ari_set_eic(pari, strtoul(optarg, NULL, 16));
+ break;
+ case ARG_POC:
+ nl_dect_ari_set_poc(pari, strtoul(optarg, NULL, 16));
+ break;
+ case ARG_GOP:
+ nl_dect_ari_set_gop(pari, strtoul(optarg, NULL, 16));
+ break;
+ case ARG_FIL:
+ nl_dect_ari_set_fil(pari, strtoul(optarg, NULL, 16));
+ break;
+ case ARG_FPS:
+ nl_dect_ari_set_fps(pari, strtoul(optarg, NULL, 16));
+ break;
+ case ARG_FPN:
+ nl_dect_ari_set_fpn(pari, strtoul(optarg, NULL, 16));
+ break;
+ }
+ }
+
+ nl_dect_cluster_set_pari(cl, pari);
+ err = nl_dect_cluster_add(sock, cl, 0);
+ if (err < 0)
+ nl_cli_fatal(err, "Unable to add cluster: %s", nl_geterror(err));
+
+ printf("Added: ");
+ nl_object_dump(OBJ_CAST(cl), &params);
+ return 0;
+}
+
diff --git a/src/dect-cluster-delete.c b/src/dect-cluster-delete.c
new file mode 100644
index 0000000..6e564ce
--- /dev/null
+++ b/src/dect-cluster-delete.c
@@ -0,0 +1,64 @@
+#include "netlink/cli/utils.h"
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct nl_dect_cluster *cl;
+ struct nl_dect_ari *pari;
+ struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_fd = stdout,
+ };
+ int err;
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_DECT);
+ cl = nl_dect_cluster_alloc();
+ pari = (void *)nl_dect_cluster_get_pari(cl);
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_NAME = 257,
+ ARG_MODE,
+ ARG_EMC,
+ ARG_FPN,
+ };
+ static struct option long_opts[] = {
+ { "name", 1, 0, ARG_NAME },
+ { "mode", 1, 0, ARG_MODE },
+ { "emc", 1, 0, ARG_EMC },
+ { "fpn", 1, 0, ARG_FPN },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "qhvd:n:t:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'v': nl_cli_print_version(); break;
+ case ARG_NAME:
+ nl_dect_cluster_set_name(cl, strdup(optarg));
+ break;
+ case ARG_MODE:
+ nl_dect_cluster_set_mode(cl, atoi(optarg));
+ break;
+ case ARG_EMC:
+ nl_dect_ari_set_emc(pari, strtoul(optarg, NULL, 16));
+ break;
+ case ARG_FPN:
+ nl_dect_ari_set_fpn(pari, strtoul(optarg, NULL, 16));
+ break;
+ }
+ }
+
+ err = nl_dect_cluster_delete(sock, cl, 0);
+ if (err < 0)
+ nl_cli_fatal(err, "Unable to delete cluster: %s", nl_geterror(err));
+
+ printf("Deleted: ");
+ nl_object_dump(OBJ_CAST(cl), &params);
+ return 0;
+}
+
diff --git a/src/dect-cluster-list.c b/src/dect-cluster-list.c
new file mode 100644
index 0000000..edff5fe
--- /dev/null
+++ b/src/dect-cluster-list.c
@@ -0,0 +1,20 @@
+#include "netlink/cli/utils.h"
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct nl_cache *cluster_cache;
+ struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_fd = stdout,
+ };
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_DECT);
+ if (nl_dect_cluster_alloc_cache(sock, &cluster_cache))
+ exit(1);
+
+ nl_cache_dump(cluster_cache, &params);
+ return 0;
+}
+
diff --git a/src/dect-llme-mac-con.c b/src/dect-llme-mac-con.c
new file mode 100644
index 0000000..5acf6a7
--- /dev/null
+++ b/src/dect-llme-mac-con.c
@@ -0,0 +1,111 @@
+#if 0
+#include "utils.h"
+
+static void obj_input(struct nl_object *obj, void *arg)
+{
+ struct nl_dump_params dp = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_fd = stdout,
+ };
+
+ printf("MAC Connection: ");
+ nl_object_dump(obj, &dp);
+ printf("\n");
+}
+
+static int event_input(struct nl_msg *msg, void *arg)
+{
+ if (nl_msg_parse(msg, &obj_input, NULL) < 0)
+ fprintf(stderr, "Unknown message type\n");
+ return NL_STOP;
+}
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct nl_cache *cluster_cache;
+ struct dect_llme_msg *lmsg;
+ struct dect_ari *ari;
+ struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_fd = stdout,
+ };
+ int index;
+ int err;
+
+ sock = nlt_alloc_socket();
+ nlt_connect(sock, NETLINK_DECT);
+ if (dect_cluster_alloc_cache(sock, &cluster_cache))
+ exit(1);
+ nl_cache_mngt_provide(cluster_cache);
+
+ lmsg = dect_llme_msg_alloc();
+ dect_llme_msg_set_type(lmsg, DECT_LLME_MAC_CON);
+
+ ari = (void *)dect_llme_mac_con_get_ari(lmsg);
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_CLUSTER = 257,
+ ARG_MCEI,
+ ARG_PMID,
+ ARG_EMC,
+ ARG_FPN,
+ ARG_FPC,
+ };
+ static struct option long_opts[] = {
+ { "cluster", 1, 0, ARG_CLUSTER },
+ { "mcei", 1, 0, ARG_MCEI },
+ { "pmid", 1, 0, ARG_PMID },
+ { "emc", 1, 0, ARG_EMC },
+ { "fpn", 1, 0, ARG_FPN },
+ { "fpc", 0, 0, ARG_FPC },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "qhvd:n:t:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'v': nlt_print_version(); break;
+ case ARG_CLUSTER:
+ index = dect_cluster_name2i(cluster_cache, optarg);
+ dect_llme_msg_set_index(lmsg, index);
+ break;
+ case ARG_MCEI:
+ dect_llme_mac_con_set_mcei(lmsg, strtoul(optarg, NULL, 16));
+ break;
+ case ARG_PMID:
+ dect_llme_mac_con_set_pmid(lmsg, strtoul(optarg, NULL, 16));
+ break;
+ case ARG_EMC:
+ dect_ari_set_emc(ari, strtoul(optarg, NULL, 16));
+ break;
+ case ARG_FPN:
+ dect_ari_set_fpn(ari, strtoul(optarg, NULL, 16));
+ break;
+ case ARG_FPC:
+ dect_llme_mac_info_set_fpc(lmsg, 0);
+ dect_llme_mac_info_set_ehlc(lmsg, 0);
+ break;
+ }
+ }
+
+ //dect_llme_mac_con_set_ari(lmsg, ari);
+ if ((err = dect_llme_request(sock, lmsg)) < 0)
+ fatal(err, "Unable to send request: %s", nl_geterror(err));
+
+ printf("Requested: ");
+ nl_object_dump(OBJ_CAST(lmsg), &params);
+
+ nl_socket_add_membership(sock, DECTNLGRP_LLME);
+ nl_socket_disable_seq_check(sock);
+ nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL);
+ while (1)
+ nl_recvmsgs_default(sock);
+ return 0;
+}
+#endif
+int main() {}
diff --git a/src/dect-llme-monitor.c b/src/dect-llme-monitor.c
new file mode 100644
index 0000000..eba1536
--- /dev/null
+++ b/src/dect-llme-monitor.c
@@ -0,0 +1,37 @@
+#include "netlink/cli/utils.h"
+
+static void obj_input(struct nl_object *obj, void *arg)
+{
+ struct nl_dump_params dp = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_fd = stdout,
+ };
+ nl_object_dump(obj, &dp);
+}
+
+static int event_input(struct nl_msg *msg, void *arg)
+{
+ if (nl_msg_parse(msg, &obj_input, NULL) < 0)
+ fprintf(stderr, "Unknown message type\n");
+ return NL_STOP;
+}
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct nl_cache *cluster_cache;
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_DECT);
+ if (nl_dect_cluster_alloc_cache(sock, &cluster_cache))
+ exit(1);
+ nl_cache_mngt_provide(cluster_cache);
+
+ nl_socket_add_membership(sock, DECTNLGRP_LLME);
+ nl_socket_disable_seq_check(sock);
+ nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL);
+ while (1)
+ nl_recvmsgs_default(sock);
+ return 0;
+}
+
diff --git a/src/dect-llme-scan.c b/src/dect-llme-scan.c
new file mode 100644
index 0000000..1d4980f
--- /dev/null
+++ b/src/dect-llme-scan.c
@@ -0,0 +1,139 @@
+#include "netlink/cli/utils.h"
+
+#define CACHE_SIZE 64
+
+static struct {
+ unsigned int index;
+ unsigned int used;
+ struct {
+ uint16_t emc;
+ uint32_t fpn;
+ uint8_t rpn;
+ } ids[CACHE_SIZE];
+} cache;
+
+static void add_ari(const struct nl_dect_llme_msg *lmsg)
+{
+ const struct nl_dect_ari *ari = nl_dect_llme_mac_info_get_pari(lmsg);
+ unsigned int index = cache.index;
+
+ cache.ids[index].emc = nl_dect_ari_get_emc(ari);
+ cache.ids[index].fpn = nl_dect_ari_get_fpn(ari);
+ cache.ids[index].rpn = nl_dect_llme_mac_info_get_rpn(lmsg);
+
+ if (++cache.used > CACHE_SIZE)
+ cache.used = CACHE_SIZE;
+ if (++cache.index == CACHE_SIZE)
+ cache.index = 0;
+}
+
+static bool lookup_ari(const struct nl_dect_llme_msg *lmsg)
+{
+ const struct nl_dect_ari *ari = nl_dect_llme_mac_info_get_pari(lmsg);
+ unsigned int index;
+
+ for (index = 0; index < cache.used; index++) {
+ if (cache.ids[index].emc == nl_dect_ari_get_emc(ari) &&
+ cache.ids[index].fpn == nl_dect_ari_get_fpn(ari) &&
+ cache.ids[index].rpn == nl_dect_llme_mac_info_get_rpn(lmsg))
+ return true;
+ }
+ return false;
+}
+
+static void obj_input(struct nl_object *obj, void *arg)
+{
+ struct nl_dect_llme_msg *lmsg = nl_object_priv(obj);
+ struct nl_dump_params dp = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_fd = stdout,
+ };
+ static unsigned int n;
+
+ if (lookup_ari(lmsg))
+ return;
+ add_ari(lmsg);
+
+ printf("%u: Station: ", ++n);
+ nl_object_dump(obj, &dp);
+ printf("\n");
+}
+
+static int event_input(struct nl_msg *msg, void *arg)
+{
+ if (nl_msg_parse(msg, &obj_input, NULL) < 0)
+ fprintf(stderr, "Unknown message type\n");
+ return NL_OK;
+}
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct nl_cache *cluster_cache;
+ struct nl_dect_llme_msg *lmsg;
+ struct nl_dect_ari *pari;
+ struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_fd = stdout,
+ };
+ int index;
+ int err;
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_DECT);
+ if (nl_dect_cluster_alloc_cache(sock, &cluster_cache))
+ exit(1);
+ nl_cache_mngt_provide(cluster_cache);
+
+ lmsg = nl_dect_llme_msg_alloc();
+ nl_dect_llme_msg_set_type(lmsg, DECT_LLME_SCAN);
+
+ pari = (void *)nl_dect_llme_mac_info_get_pari(lmsg);
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_CLUSTER = 257,
+ ARG_EMC,
+ ARG_FPN,
+ };
+ static struct option long_opts[] = {
+ { "cluster", 1, 0, ARG_CLUSTER },
+ { "emc", 1, 0, ARG_EMC },
+ { "fpn", 1, 0, ARG_FPN },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "qhvd:n:t:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'v': nl_cli_print_version(); break;
+ case ARG_CLUSTER:
+ index = nl_dect_cluster_name2i(cluster_cache, optarg);
+ nl_dect_llme_msg_set_index(lmsg, index);
+ break;
+ case ARG_EMC:
+ nl_dect_ari_set_emc(pari, strtoul(optarg, NULL, 16));
+ break;
+ case ARG_FPN:
+ nl_dect_ari_set_fpn(pari, strtoul(optarg, NULL, 16));
+ break;
+ }
+ }
+
+ //nl_dect_llme_mac_info_set_pari(lmsg, pari);
+ if ((err = nl_dect_llme_request(sock, lmsg)) < 0)
+ nl_cli_fatal(err, "Unable to send request: %s", nl_geterror(err));
+
+ printf("Requested: ");
+ nl_object_dump(OBJ_CAST(lmsg), &params);
+
+ nl_socket_disable_seq_check(sock);
+ nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL);
+ while (1)
+ nl_recvmsgs_default(sock);
+ return 0;
+}
+
diff --git a/src/dect-transceiver-bind.c b/src/dect-transceiver-bind.c
new file mode 100644
index 0000000..97ad77e
--- /dev/null
+++ b/src/dect-transceiver-bind.c
@@ -0,0 +1,62 @@
+#include "netlink/cli/utils.h"
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct nl_dect_transceiver *trx;
+ struct nl_cache *cell_cache;
+ struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_fd = stdout,
+ };
+ int err;
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_DECT);
+
+ if (nl_dect_cell_alloc_cache(sock, &cell_cache))
+ exit(1);
+ nl_cache_mngt_provide(cell_cache);
+
+ trx = nl_dect_transceiver_alloc();
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_TRANSCEIVER = 257,
+ ARG_CELL,
+ ARG_UNBIND,
+ };
+ static struct option long_opts[] = {
+ { "transceiver", 1, 0, ARG_TRANSCEIVER },
+ { "cell", 1, 0, ARG_CELL },
+ { "unbind", 0, 0, ARG_UNBIND },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "qhvd:n:t:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'v': nl_cli_print_version(); break;
+ case ARG_TRANSCEIVER:
+ nl_dect_transceiver_set_name(trx, strdup(optarg));
+ break;
+ case ARG_CELL:
+ nl_dect_transceiver_set_link(trx, nl_dect_cell_name2i(cell_cache, optarg));
+ break;
+ case ARG_UNBIND:
+ nl_dect_transceiver_set_link(trx, -1);
+ break;
+ }
+ }
+
+ err = nl_dect_transceiver_change(sock, trx, 0);
+ if (err < 0)
+ nl_cli_fatal(err, "Unable to bind to cell: %s", nl_geterror(err));
+
+ printf("Bound: ");
+ nl_object_dump(OBJ_CAST(trx), &params);
+ return 0;
+}
+
diff --git a/src/dect-transceiver-list.c b/src/dect-transceiver-list.c
new file mode 100644
index 0000000..c1e5cde
--- /dev/null
+++ b/src/dect-transceiver-list.c
@@ -0,0 +1,60 @@
+#include "netlink/cli/utils.h"
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct nl_cache *cell_cache;
+ struct nl_cache *transceiver_cache;
+ struct nl_dect_transceiver *trx;
+ struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_fd = stdout,
+ };
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_DECT);
+
+ if (nl_dect_cell_alloc_cache(sock, &cell_cache))
+ exit(1);
+ nl_cache_mngt_provide(cell_cache);
+
+ if (nl_dect_transceiver_alloc_cache(sock, &transceiver_cache))
+ exit(1);
+ trx = nl_dect_transceiver_alloc();
+ if (trx == NULL)
+ exit(1);
+
+ for (;;) {
+ int c, optidx = 0, cidx;
+ enum {
+ ARG_NAME = 257,
+ ARG_CELL,
+ };
+ static struct option long_ops[] = {
+ { "name", 1, 0, ARG_NAME },
+ { "cell", 1, 0, ARG_CELL },
+ {}
+ };
+
+ c = getopt_long(argc, argv, "n:c:", long_ops, &optidx);
+ if (c == -1)
+ break;
+ switch (c) {
+ case ARG_NAME:
+ nl_dect_transceiver_set_name(trx, optarg);
+ break;
+ case ARG_CELL:
+ cidx = nl_dect_cell_name2i(cell_cache, optarg);
+ if (cidx == 0) {
+ fprintf(stderr, "cell %s does not exist\n", optarg);
+ exit(1);
+ }
+ nl_dect_transceiver_set_link(trx, cidx);
+ break;
+ }
+ }
+
+ nl_cache_dump_filter(transceiver_cache, &params, OBJ_CAST(trx));
+ return 0;
+}
+