dect: add DECT protocol core
Add an (so far unfunctional) DECT protocol core and a basic DECT netlink socket family. Signed-off-by: Patrick McHardy <kaber@trash.net>
This commit is contained in:
parent
6c2fa86bf4
commit
28c157ecb4
|
@ -53,6 +53,8 @@ header-y += cgroupstats.h
|
|||
header-y += cramfs_fs.h
|
||||
header-y += cycx_cfm.h
|
||||
header-y += dcbnl.h
|
||||
header-y += dect.h
|
||||
header-y += dect_netlink.h
|
||||
header-y += dlmconstants.h
|
||||
header-y += dlm_device.h
|
||||
header-y += dlm_netlink.h
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
#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;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sockaddr_dect_ssap
|
||||
*
|
||||
* @dect_family: family (AF_DECT)
|
||||
* @dect_dlei: Data Link Endpoint Identifier
|
||||
* @dect_class: Class A/B
|
||||
*/
|
||||
struct sockaddr_dect_ssap {
|
||||
sa_family_t dect_family;
|
||||
__u64 dect_ari:40,
|
||||
dect_pmid:20,
|
||||
dect_lcn:3;
|
||||
__u8 dect_lln:4,
|
||||
dect_sapi:4;
|
||||
__u8 dect_class;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
__u64 dect_ari:40,
|
||||
dect_pmid:20,
|
||||
dect_lcn:3;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_DECT_H */
|
|
@ -0,0 +1,255 @@
|
|||
#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_attrs {
|
||||
DECTA_SLOT_UNSPEC,
|
||||
DECTA_SLOT_NUM,
|
||||
DECTA_SLOT_STATE,
|
||||
DECTA_SLOT_CARRIER,
|
||||
DECTA_SLOT_FREQUENCY,
|
||||
DECTA_SLOT_RSSI,
|
||||
DECTA_SLOT_RX_PACKETS,
|
||||
DECTA_SLOT_RX_BYTES,
|
||||
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)
|
||||
|
||||
enum dect_transceiver_attrs {
|
||||
DECTA_TRANSCEIVER_UNSPEC,
|
||||
DECTA_TRANSCEIVER_NAME,
|
||||
DECTA_TRANSCEIVER_TYPE,
|
||||
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),
|
||||
};
|
||||
|
||||
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_cluster_attrs {
|
||||
DECTA_CLUSTER_UNSPEC,
|
||||
DECTA_CLUSTER_NAME,
|
||||
DECTA_CLUSTER_MODE,
|
||||
DECTA_CLUSTER_PARI,
|
||||
DECTA_CLUSTER_CELLS,
|
||||
__DECTA_CLUSTER_MAX
|
||||
};
|
||||
#define DECTA_CLUSTER_MAX (__DECTA_CLUSTER_MAX - 1)
|
||||
|
||||
enum dect_cluster_modes {
|
||||
DECT_MODE_FP,
|
||||
DECT_MODE_PP,
|
||||
DECT_MODE_MONITOR,
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 = 0x8000,
|
||||
DECT_FPC_FULL_SLOT = 0x4000,
|
||||
DECT_FPC_FREQ_CONTROL = 0x2000,
|
||||
DECT_FPC_PAGE_REPETITION = 0x1000,
|
||||
DECT_FPC_CO_SETUP_ON_DUMMY = 0x800,
|
||||
DECT_FPC_CL_UPLINK = 0x400,
|
||||
DECT_FPC_CL_DOWNLINK = 0x200,
|
||||
DECT_FPC_BASIC_A_FIELD_SETUP = 0x100,
|
||||
DECT_FPC_ADV_A_FIELD_SETUP = 0x80,
|
||||
DECT_FPC_B_FIELD_SETUP = 0x40,
|
||||
DECT_FPC_CF_MESSAGES = 0x20,
|
||||
DECT_FPC_IN_MIN_DELAY = 0x10,
|
||||
DECT_FPC_IN_NORM_DELAY = 0x8,
|
||||
DECT_FPC_IP_ERROR_DETECTION = 0x4,
|
||||
DECT_FPC_IP_ERROR_CORRECTION = 0x2,
|
||||
DECT_FPC_MULTIBEARER_CONNECTIONS = 0x1,
|
||||
};
|
||||
|
||||
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 = 0x800,
|
||||
DECT_HLC_STANDARD_CIPHERING = 0x400,
|
||||
DECT_HLC_LOCATION_REGISTRATION = 0x200,
|
||||
DECT_HLC_SIM_SERVICES = 0x100,
|
||||
DECT_HLC_NON_STATIC_FIXED_PART = 0x80,
|
||||
DECT_HLC_CISS_SERVICE = 0x40,
|
||||
DECT_HLC_CLMS_SERVICE = 0x20,
|
||||
DECT_HLC_COMS_SERVICE = 0x10,
|
||||
DECT_HLC_ACCESS_RIGHT_REQUESTS = 0x8,
|
||||
DECT_HLC_EXTERNAL_HANDOVER = 0x4,
|
||||
DECT_HLC_CONNECTION_HANDOVER = 0x2,
|
||||
DECT_HLC_RESERVED = 0x1,
|
||||
};
|
||||
|
||||
enum dect_extended_higher_layer_capabilities {
|
||||
DECT_EHLC_ISDN_DATA_SERVICE = 0x1,
|
||||
DECT_EHLC_DATA_SERVICE_PROFILE_A_B = 0x2,
|
||||
DECT_EHLC_DATA_SERVICE_PROFILE_C = 0x4,
|
||||
DECT_EHLC_DATA_SERVICE_PROFILE_D = 0x8,
|
||||
DECT_EHLC_DATA_SERVICE_PROFILE_E = 0x10,
|
||||
DECT_EHLC_DATA_SERVICE_PROFILE_F = 0x20,
|
||||
DECT_EHLC_ASYMETRIC_BEARERS = 0x40,
|
||||
DECT_EHLC_TPUI_LOCATION_REGISTRATION = 0x100,
|
||||
};
|
||||
|
||||
enum dect_efpc_attrs {
|
||||
DECTA_EFPC_UNSPEC,
|
||||
DECTA_EFPC_CRFP_HOPS,
|
||||
DECTA_EFPC_CRFP_ENCRYPTION,
|
||||
DECTA_EFPC_REP_HOPS,
|
||||
DECTA_EFPC_REP_INTERLACING,
|
||||
DECTA_EFPC_EHLC,
|
||||
__DECTA_EFPC_MAX
|
||||
};
|
||||
#define DECTA_EFPC_MAX (__DECTA_EFPC_MAX - 1)
|
||||
|
||||
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_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_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 */
|
|
@ -24,6 +24,7 @@
|
|||
/* leave room for NETLINK_DM (DM Events) */
|
||||
#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */
|
||||
#define NETLINK_ECRYPTFS 19
|
||||
#define NETLINK_DECT 20 /* DECT */
|
||||
|
||||
#define MAX_LINKS 32
|
||||
|
||||
|
|
|
@ -194,7 +194,8 @@ struct ucred {
|
|||
#define AF_RXRPC 33 /* RxRPC sockets */
|
||||
#define AF_ISDN 34 /* mISDN sockets */
|
||||
#define AF_PHONET 35 /* Phonet sockets */
|
||||
#define AF_MAX 36 /* For now.. */
|
||||
#define AF_DECT 36 /* DECT sockets */
|
||||
#define AF_MAX 37 /* For now.. */
|
||||
|
||||
/* Protocol families, same as address families. */
|
||||
#define PF_UNSPEC AF_UNSPEC
|
||||
|
@ -233,6 +234,7 @@ struct ucred {
|
|||
#define PF_RXRPC AF_RXRPC
|
||||
#define PF_ISDN AF_ISDN
|
||||
#define PF_PHONET AF_PHONET
|
||||
#define PF_DECT AF_DECT
|
||||
#define PF_MAX AF_MAX
|
||||
|
||||
/* Maximum queue length specifiable by listen. */
|
||||
|
@ -303,6 +305,7 @@ struct ucred {
|
|||
#define SOL_BLUETOOTH 274
|
||||
#define SOL_PNPIPE 275
|
||||
#define SOL_RDS 276
|
||||
#define SOL_DECT 277
|
||||
|
||||
/* IPX options */
|
||||
#define IPX_TYPE 1
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* DECT MAC Layer - Cell Control Protocol (CCP)
|
||||
*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*/
|
||||
|
||||
#ifndef _NET_DECT_CCP
|
||||
#define _NET_DECT_CCP
|
||||
|
||||
#define DECT_CCP_TIPC_TYPE TIPC_RESERVED_TYPES
|
||||
#define DECT_CCP_CELL_PORT 1000
|
||||
#define DECT_CCP_CLUSTER_PORT_BASE 1000
|
||||
|
||||
enum dect_ccp_primitives {
|
||||
/* CCF -> CSF */
|
||||
DECT_CCP_SET_MODE,
|
||||
DECT_CCP_SCAN,
|
||||
DECT_CCP_ENABLE,
|
||||
DECT_CCP_PRELOAD,
|
||||
DECT_CCP_TBC_INITIATE,
|
||||
DECT_CCP_TBC_CONFIRM,
|
||||
DECT_CCP_TBC_RELEASE,
|
||||
/* CSF -> CCF */
|
||||
DECT_CCP_MBC_CONN_INDICATE,
|
||||
DECT_CCP_MBC_CONN_NOTIFY,
|
||||
DECT_CCP_MBC_DATA_INDICATE,
|
||||
};
|
||||
|
||||
struct dect_ccp_msg_hdr {
|
||||
u8 primitive;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dect_ccp_ari {
|
||||
__be64 ari;
|
||||
};
|
||||
|
||||
struct dect_ccp_mode_msg {
|
||||
u8 mode;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dect_ccp_scan_msg {
|
||||
__be64 ari;
|
||||
__be64 ari_mask;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dect_ccp_sysinfo_msg {
|
||||
__be64 pari;
|
||||
__be64 sari[DECT_SARI_CYCLE_MAX];
|
||||
__be64 fpc;
|
||||
__be64 hlc;
|
||||
__be64 efpc;
|
||||
__be32 mfn;
|
||||
u8 num_saris;
|
||||
u8 rpn;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dect_ccp_mbc_msg {
|
||||
__be32 mcei;
|
||||
__be32 pmid;
|
||||
__be64 ari;
|
||||
u8 ecn;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dect_ccp_data_msg {
|
||||
u8 channel;
|
||||
u8 data[];
|
||||
} __attribute__((packed));
|
||||
|
||||
extern int dect_ccp_cluster_init(struct dect_cluster *cl);
|
||||
extern void dect_ccp_cluster_shutdown(struct dect_cluster *cl);
|
||||
|
||||
extern struct dect_cluster_handle *dect_ccp_cell_init(struct dect_cell *cell,
|
||||
u8 clindex);
|
||||
|
||||
#endif /* _NET_DECT_CPP */
|
|
@ -0,0 +1,198 @@
|
|||
#ifndef _NET_DECT_DECT_H
|
||||
#define _NET_DECT_DECT_H
|
||||
|
||||
#define DECT_FRAMES_PER_MULTIFRAME 16
|
||||
|
||||
static inline u8 dect_next_framenum(u8 framenum)
|
||||
{
|
||||
if (++framenum == DECT_FRAMES_PER_MULTIFRAME)
|
||||
framenum = 0;
|
||||
return framenum;
|
||||
}
|
||||
|
||||
static inline u8 dect_framenum_add(u8 f1, u8 f2)
|
||||
{
|
||||
return (f1 + f2) % DECT_FRAMES_PER_MULTIFRAME;
|
||||
}
|
||||
|
||||
#define DECT_MULTIFRAME_MASK 0x00ffffff
|
||||
|
||||
static inline u32 dect_next_mfn(u32 mfn)
|
||||
{
|
||||
if (++mfn == (1 << 24) - 1)
|
||||
mfn = 0;
|
||||
return mfn;
|
||||
}
|
||||
|
||||
static inline u32 dect_mfn_add(u32 mfn1, u32 mfn2)
|
||||
{
|
||||
return (mfn1 + mfn2) & DECT_MULTIFRAME_MASK;
|
||||
}
|
||||
|
||||
/* Compare multiframe numbers, considering overflows */
|
||||
static inline bool dect_mfn_before(u32 mfn1, u32 mfn2)
|
||||
{
|
||||
return (s32)((mfn2 << 8) - (mfn1 << 8)) > 0;
|
||||
}
|
||||
|
||||
static inline bool dect_mfn_after(u32 mfn1, u32 mfn2)
|
||||
{
|
||||
return dect_mfn_before(mfn2, mfn1);
|
||||
}
|
||||
|
||||
#include <linux/dect.h>
|
||||
#include <net/dect/identities.h>
|
||||
#include <net/dect/mac_ccf.h>
|
||||
#include <net/dect/dlc.h>
|
||||
|
||||
extern void __acquires(dect_cfg_mutex) dect_lock(void);
|
||||
extern void __releases(dect_cfg_mutex) dect_unlock(void);
|
||||
|
||||
/**
|
||||
* struct dect_cluster - DECT cluster of up to 8/256 cells
|
||||
*
|
||||
* @list: device list node
|
||||
* @name: device identifier
|
||||
* @index: unique numeric cluster identifier
|
||||
* @mode: device mode (FP/PP/monitor)
|
||||
* @pari: primary access rights identifier
|
||||
* @si: system information
|
||||
* @bmc: Broadcast Message Control
|
||||
* @cmc: Connectionless Message Control
|
||||
* @mbcs: Multi-Bearer Controllers
|
||||
* @cells: DECT cells
|
||||
*/
|
||||
struct dect_cluster {
|
||||
struct list_head list;
|
||||
char name[DECTNAMSIZ];
|
||||
int index;
|
||||
|
||||
u32 tipc_id;
|
||||
u32 tipc_portref;
|
||||
struct dect_cluster_handle handle;
|
||||
|
||||
enum dect_cluster_modes mode;
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
struct dect_ari pari;
|
||||
struct dect_si si;
|
||||
|
||||
u32 pmid;
|
||||
|
||||
struct list_head cells;
|
||||
struct dect_bmc bmc;
|
||||
struct dect_cmc cmc;
|
||||
struct list_head mbcs;
|
||||
|
||||
u32 mcei_rover;
|
||||
struct list_head mac_connections;
|
||||
};
|
||||
|
||||
extern void dect_cluster_init(struct dect_cluster *cl);
|
||||
extern void dect_cluster_shutdown(struct dect_cluster *cl);
|
||||
|
||||
extern struct dect_cluster *dect_cluster_get_by_index(int index);
|
||||
extern struct dect_cluster *dect_cluster_get_by_pari(const struct dect_ari *ari);
|
||||
|
||||
/**
|
||||
* struct dect_llme_req - LLME netlink request
|
||||
*
|
||||
* @nlh: netlink header
|
||||
* @nlpid: netlink socket PID
|
||||
*/
|
||||
struct dect_llme_req {
|
||||
struct nlmsghdr nlh;
|
||||
u32 nlpid;
|
||||
};
|
||||
|
||||
struct dect_scan_result;
|
||||
extern void dect_llme_scan_result_notify(const struct dect_cluster *cl,
|
||||
const struct dect_scan_result *res);
|
||||
|
||||
#include <net/sock.h>
|
||||
|
||||
extern const struct proto_ops dect_stream_ops;
|
||||
extern const struct proto_ops dect_dgram_ops;
|
||||
|
||||
struct dect_proto {
|
||||
unsigned int type;
|
||||
unsigned int protocol;
|
||||
int capability;
|
||||
const struct proto_ops *ops;
|
||||
int (*getname)(struct sock *sk,
|
||||
struct sockaddr *uaddr, int *len,
|
||||
int peer);
|
||||
struct proto proto;
|
||||
};
|
||||
|
||||
#include <net/tcp_states.h>
|
||||
|
||||
enum {
|
||||
DECT_SK_ESTABLISHED = TCP_ESTABLISHED,
|
||||
DECT_SK_ESTABLISH_PENDING = TCP_SYN_SENT,
|
||||
DECT_SK_RELEASED = TCP_CLOSE,
|
||||
DECT_SK_RELEASE_PENDING = TCP_CLOSING,
|
||||
DECT_SK_LISTEN = TCP_LISTEN,
|
||||
};
|
||||
|
||||
struct dect_csk {
|
||||
struct sock sk;
|
||||
struct hlist_head accept_queue;
|
||||
};
|
||||
|
||||
static inline struct dect_csk *dect_csk(const struct sock *sk)
|
||||
{
|
||||
return (struct dect_csk *)sk;
|
||||
}
|
||||
|
||||
extern int dect_proto_register(struct dect_proto *proto);
|
||||
extern void dect_proto_unregister(struct dect_proto *proto);
|
||||
|
||||
struct dect_skb_sk_cb {
|
||||
//struct dect_skb_trx_cb cb;
|
||||
int index;
|
||||
};
|
||||
|
||||
#define DECT_SK_CB(skb) ((struct dect_skb_sk_cb *)(skb)->cb)
|
||||
|
||||
static inline int dect_sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
/*
|
||||
* Release the transceiver reference, it is only valid in IRQ and
|
||||
* softirq context.
|
||||
*/
|
||||
//FIXME
|
||||
//DECT_SK_CB(skb)->index = DECT_CB(skb)->trx->dev->index;
|
||||
return sock_queue_rcv_skb(sk, skb);
|
||||
}
|
||||
|
||||
extern void (*dect_raw_rcv_hook)(struct sk_buff *skb);
|
||||
static inline void dect_raw_rcv(struct sk_buff *skb)
|
||||
{
|
||||
typeof(dect_raw_rcv_hook) dect_raw_rcv;
|
||||
|
||||
rcu_read_lock();
|
||||
dect_raw_rcv = dect_raw_rcv_hook;
|
||||
if (dect_raw_rcv != NULL)
|
||||
dect_raw_rcv(skb);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
extern int dect_af_module_init(void);
|
||||
extern void dect_af_module_exit(void);
|
||||
|
||||
extern int dect_bsap_module_init(void);
|
||||
extern void dect_bsap_module_exit(void);
|
||||
extern int dect_ssap_module_init(void);
|
||||
extern void dect_ssap_module_exit(void);
|
||||
|
||||
extern int dect_lu1_sap_module_init(void);
|
||||
extern void dect_lu1_sap_module_exit(void);
|
||||
|
||||
extern int dect_netlink_module_init(void);
|
||||
extern void dect_netlink_module_exit(void);
|
||||
|
||||
extern struct sk_buff *skb_append_frag(struct sk_buff *head, struct sk_buff *skb);
|
||||
|
||||
#endif /* _NET_DECT_DECT_H */
|
|
@ -0,0 +1,428 @@
|
|||
/*
|
||||
* DECT DLC Layer
|
||||
*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*/
|
||||
|
||||
#ifndef _NET_DECT_DLC_H
|
||||
#define _NET_DECT_DLC_H
|
||||
|
||||
/*
|
||||
* C-Plane data link service
|
||||
*/
|
||||
|
||||
/*
|
||||
* FA-Frame
|
||||
*/
|
||||
|
||||
#define DECT_FA_HDR_SIZE 3
|
||||
|
||||
struct dect_fa_hdr {
|
||||
u8 addr;
|
||||
u8 ctrl;
|
||||
u8 li;
|
||||
};
|
||||
|
||||
/*
|
||||
* Address field
|
||||
*/
|
||||
|
||||
#define DECT_FA_ADDR_OFF 0
|
||||
|
||||
/* New link flag */
|
||||
#define DECT_FA_ADDR_NLF_FLAG 0x80
|
||||
|
||||
/* Logical Link Number */
|
||||
#define DECT_FA_ADDR_LLN_MASK 0x70
|
||||
#define DECT_FA_ADDR_LLN_SHIFT 4
|
||||
|
||||
/* Service Access Point Identifier */
|
||||
#define DECT_FA_ADDR_SAPI_MASK 0x0c
|
||||
#define DECT_FA_ADDR_SAPI_SHIFT 2
|
||||
|
||||
/* Command/Response flag */
|
||||
#define DECT_FA_ADDR_CR_FLAG 0x02
|
||||
|
||||
/* Reserved bit */
|
||||
#define DECT_FA_ADDR_RES_BIT 0x01
|
||||
|
||||
/*
|
||||
* Control field
|
||||
*/
|
||||
|
||||
#define DECT_FA_CTRL_OFF 1
|
||||
|
||||
/*
|
||||
* I-Format: numbered information
|
||||
*/
|
||||
|
||||
#define DECT_FA_CTRL_I_FMT_MASK 0x01
|
||||
#define DECT_FA_CTRL_I_FMT_ID 0x00
|
||||
|
||||
/* Receive sequence number */
|
||||
#define DECT_FA_CTRL_I_NR_MASK 0xe0
|
||||
#define DECT_FA_CTRL_I_NR_SHIFT 5
|
||||
|
||||
/* Poll bit */
|
||||
#define DECT_FA_CTRL_I_P_FLAG 0x10
|
||||
|
||||
/* Send sequence number */
|
||||
#define DECT_FA_CTRL_I_NS_MASK 0x0e
|
||||
#define DECT_FA_CTRL_I_NS_SHIFT 1
|
||||
|
||||
/* Command */
|
||||
#define DECT_FA_CTRL_I_CMD_I (0x0)
|
||||
|
||||
/*
|
||||
* S-Format: supervisory functions
|
||||
*/
|
||||
|
||||
#define DECT_FA_CTRL_S_FMT_MASK 0x03
|
||||
#define DECT_FA_CTRL_S_FMT_ID 0x01
|
||||
|
||||
/* Receive sequence number */
|
||||
#define DECT_FA_CTRL_S_NR_MASK 0xe0
|
||||
#define DECT_FA_CTRL_S_NR_SHIFT 5
|
||||
|
||||
/* Poll/final bit */
|
||||
#define DECT_FA_CTRL_S_PF_FLAG 0x10
|
||||
|
||||
/* Command/Response */
|
||||
#define DECT_FA_CTRL_S_CR_MASK 0x0c
|
||||
|
||||
#define DECT_FA_CTRL_S_CR_RR 0x00
|
||||
#define DECT_FA_CTRL_S_CR_RNR 0x40
|
||||
#define DECT_FA_CTRL_S_CR_REJ 0x80
|
||||
|
||||
/*
|
||||
* U-Format: unnumbered information
|
||||
*/
|
||||
|
||||
#define DECT_FA_CTRL_U_FMT_MASK 0x03
|
||||
#define DECT_FA_CTRL_U_FMT_ID 0x03
|
||||
|
||||
/* Unnumbered function bits */
|
||||
#define DECT_FA_CTRL_U_U1_MASK 0xec
|
||||
|
||||
/* Poll/final bit */
|
||||
#define DECT_FA_CTRL_U_PF_FLAG 0x10
|
||||
|
||||
/* Command/Response */
|
||||
#define DECT_FA_CTRL_U_CR_MASK 0xef
|
||||
|
||||
#define DECT_FA_CTRL_U_CR_SABM 0x2c
|
||||
#define DECT_FA_CTRL_U_CR_DM 0x0c
|
||||
#define DECT_FA_CTRL_U_CR_UI 0x00
|
||||
#define DECT_FA_CTRL_U_CR_DISC 0x40
|
||||
#define DECT_FA_CTRL_U_CR_UA 0x60
|
||||
|
||||
/*
|
||||
* Length Indicator
|
||||
*/
|
||||
|
||||
#define DECT_FA_LI_OFF 2
|
||||
|
||||
/* Length (octets) */
|
||||
#define DECT_FA_LI_LENGTH_MASK 0xfc
|
||||
#define DECT_FA_LI_LENGTH_SHIFT 2
|
||||
|
||||
/* More data flag */
|
||||
#define DECT_FA_LI_M_FLAG 0x02
|
||||
|
||||
/* Extended length indicator bit */
|
||||
#define DECT_FA_LI_EXT_FLAG 0x01
|
||||
|
||||
/* maximum length value */
|
||||
#define DECT_FA_LI_MAX 63
|
||||
|
||||
/*
|
||||
* Extended Length indicator
|
||||
*/
|
||||
|
||||
#define DECT_FA_ELI_OFF 3
|
||||
|
||||
/* Length (octets) */
|
||||
#define DECT_FA_ELI_LENGTH_MASK 0xfc
|
||||
#define DECT_FA_ELI_LENGTH_SHIFT 2
|
||||
|
||||
struct dect_fa_len {
|
||||
u8 len;
|
||||
bool more;
|
||||
};
|
||||
|
||||
/*
|
||||
* Fill Field
|
||||
*/
|
||||
|
||||
#define DECT_FA_FILL_PATTERN 0xf0
|
||||
|
||||
/*
|
||||
* Checksum field
|
||||
*/
|
||||
|
||||
#define DECT_FA_CSUM_SIZE 2
|
||||
|
||||
/*
|
||||
* Information field
|
||||
*/
|
||||
|
||||
#define DECT_FA_I_MAX (DECT_FA_LI_MAX - DECT_FA_HDR_SIZE - DECT_FA_CSUM_SIZE)
|
||||
|
||||
|
||||
/**
|
||||
* struct dect_dli - DECT Data Link Identifier (DLI)
|
||||
*
|
||||
* @lln: Logical Link Number
|
||||
* @mci: Mac Connection Identifier
|
||||
*/
|
||||
struct dect_dli {
|
||||
enum dect_llns lln;
|
||||
struct dect_mci mci;
|
||||
};
|
||||
|
||||
/**
|
||||
* @DECT_LAPC_ULI: unassigned link identifier state (class U/A)
|
||||
* @DECT_LAPC_ALI: assigned link identifier state (class B established)
|
||||
* @DECT_LAPC_ASM: assigned Link Identifier/multiple frame state (class B suspended)
|
||||
*/
|
||||
enum dect_lapc_states {
|
||||
DECT_LAPC_ULI,
|
||||
DECT_LAPC_ALI,
|
||||
DECT_LAPC_ASM,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_lapc - DECT LAPC entity
|
||||
*
|
||||
* @lc: Associated Lc entity
|
||||
* @dli: Data Link Identifier
|
||||
* @sapi: Service Access Point Identifier
|
||||
* @cmd: CR bit setting for commands (PT: 1, FT: 0)
|
||||
* @nlf: New link flag
|
||||
* @v_s: Send state Variable V(S): sequence number of next I-frame
|
||||
* @v_a: Acknowledge state Variable V(A): last I-frame that has been acknowledged
|
||||
* @v_r: Receive state Variable V(R): next expected sequence number
|
||||
* @peer_busy: Peer is in receiver busy condition
|
||||
* @window: maximum number of oustanding unacknowledged I-frames
|
||||
* @mod: modulus for sequence number calculations
|
||||
* @timer: Retransmission timer (DL.04)
|
||||
* @rcnt: Retransmission counter
|
||||
*/
|
||||
struct dect_lapc {
|
||||
struct sock *sk;
|
||||
struct dect_lc *lc;
|
||||
struct dect_dli dli;
|
||||
enum dect_sapis sapi;
|
||||
|
||||
bool cmd;
|
||||
|
||||
enum dect_lapc_states state;
|
||||
bool nlf;
|
||||
u8 v_s;
|
||||
u8 v_a;
|
||||
u8 v_r;
|
||||
struct sk_buff_head retransq;
|
||||
|
||||
bool busy;
|
||||
bool peer_busy;
|
||||
|
||||
u8 window;
|
||||
u8 mod;
|
||||
u8 rcnt;
|
||||
|
||||
struct timer_list timer;
|
||||
struct sk_buff *rcv_head;
|
||||
};
|
||||
|
||||
/* class A window size and sequence number modulus */
|
||||
#define DECT_LAPC_CLASS_A_WINDOW 1
|
||||
#define DECT_LAPC_CLASS_A_MOD 2
|
||||
|
||||
/* class B window size and sequence number modulus */
|
||||
#define DECT_LAPC_CLASS_B_INITIAL_WINDOW 1
|
||||
#define DECT_LAPC_CLASS_B_WINDOW 3
|
||||
#define DECT_LAPC_CLASS_B_MOD 8
|
||||
|
||||
/* maximum number of retransmissions */
|
||||
#define DECT_LAPC_RETRANS_MAX 3
|
||||
|
||||
/* various timer parameters specified in Annex A */
|
||||
#define DECT_LAPC_CLASS_A_ESTABLISH_TIMEOUT (2 * HZ)
|
||||
#define DECT_LAPC_CLASS_B_ESTABLISH_TIMEOUT (2 * HZ)
|
||||
#define DECT_LAPC_RETRANSMISSION_TIMEOUT (1 * HZ)
|
||||
#define DECT_LAPC_LINK_RELEASE_TIMEOUT (2 * HZ)
|
||||
#define DECT_LAPC_LINK_SUSPEND_TIMEOUT (2 * HZ)
|
||||
#define DECT_LAPC_LINK_RESUME_TIMEOUT (2 * HZ)
|
||||
#define DECT_LAPC_CONNECTION_HANDOVER_TIMEOUT (10 * HZ)
|
||||
#define DECT_LAPC_CONNECTION_HANDOVER_INTERVAL (4 * HZ)
|
||||
|
||||
extern struct dect_lapc *dect_lapc_init(const struct dect_dli *dli,
|
||||
enum dect_sapis sapi,
|
||||
struct dect_lc *lc, gfp_t gfp);
|
||||
extern void dect_lapc_release(struct dect_lapc *lapc);
|
||||
|
||||
extern int dect_lapc_transmit(struct dect_lapc *lapc);
|
||||
extern int dect_lapc_establish(struct dect_lapc *lapc);
|
||||
extern struct dect_lapc *dect_ssap_rcv_request(struct dect_lc *lc,
|
||||
const struct dect_dli *dli,
|
||||
enum dect_sapis sapi);
|
||||
|
||||
/**
|
||||
* struct dect_lc - DECT Lc entity
|
||||
*
|
||||
* @mc: MAC connection
|
||||
* @lsig: link signature for checksumming (lower 16 bits of PMID or 0)
|
||||
* @rx_head: reassembly queue head
|
||||
* @rx_len: target length of current reassembly buffer
|
||||
* @txq: transmit queue
|
||||
* @tx_head: current TX LAPC frame
|
||||
* @tx_len: TX target fragment length
|
||||
* @lapcs: LAPC entities associated with the Lc
|
||||
* @e_lapc: LAPC performing establishment procedures
|
||||
*
|
||||
* The Lc entity is responsible for framing, logical channel selection and
|
||||
* fragmenting of LAPC PDUs. There is one Lc entity per MAC connection.
|
||||
*/
|
||||
struct dect_lc {
|
||||
struct dect_mac_conn *mc;
|
||||
u16 lsig;
|
||||
|
||||
struct sk_buff *rx_head;
|
||||
u8 rx_len;
|
||||
|
||||
struct sk_buff_head txq;
|
||||
struct sk_buff *tx_head;
|
||||
u8 tx_len;
|
||||
|
||||
struct dect_lapc *lapcs[DECT_LLN_MAX + 1];
|
||||
struct dect_lapc *elapc;
|
||||
};
|
||||
|
||||
#define DECT_LC_LSIG_MASK 0xffff
|
||||
|
||||
extern struct dect_lc *dect_lc_init(struct dect_mac_conn *mc, gfp_t gfp);
|
||||
|
||||
/**
|
||||
* struct dect_lb - DECT Lb entity (C-plane broadcast service)
|
||||
*
|
||||
*
|
||||
*/
|
||||
struct dect_lb {
|
||||
};
|
||||
|
||||
#define DECT_LB_SHORT_FRAME_SIZE 3
|
||||
#define DECT_LB_LONG_FRAME_SIZE 5
|
||||
#define DECT_LB_EXTENDED_FRAME_SIZE_MAX (6 * DECT_LB_LONG_FRAME_SIZE)
|
||||
|
||||
#include <net/sock.h>
|
||||
|
||||
/**
|
||||
* struct dect_dlc_fbx_ops - DLC U-plane lower (FBx) entity ops
|
||||
*
|
||||
*/
|
||||
struct dect_fbx;
|
||||
struct dect_fbx_ops {
|
||||
struct sk_buff *(*dequeue)(struct dect_fbx *fbx);
|
||||
void (*enqueue)(struct dect_fbx *fbx,
|
||||
struct sk_buff *skb);
|
||||
};
|
||||
|
||||
struct dect_fbx {
|
||||
const struct dect_fbx_ops *ops;
|
||||
};
|
||||
|
||||
extern const struct dect_fbx_ops dect_fbn_ops;
|
||||
|
||||
struct dect_lux;
|
||||
struct dect_lux_ops {
|
||||
struct sk_buff *(*dequeue)(struct dect_lux *lux);
|
||||
void (*enqueue)(struct dect_lux *lux,
|
||||
struct sk_buff *skb);
|
||||
void (*disconnect)(struct dect_lux *lux);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_lux - DLC U-plane upper (LUx) entity
|
||||
*
|
||||
* @fpx: FBx entity
|
||||
*/
|
||||
struct dect_lux {
|
||||
const struct dect_lux_ops *ops;
|
||||
struct dect_fbx fbx;
|
||||
};
|
||||
|
||||
/**
|
||||
* dect_mac_connection_states - DECT MAC connection states as viewed by the DLC
|
||||
*
|
||||
* @DECT_MAC_CONN_CLOSED:
|
||||
* @DECT_MAC_CONN_OPEN_PENDING:
|
||||
* @DECT_MAC_CONN_OPEN:
|
||||
*/
|
||||
enum dect_mac_conn_states {
|
||||
DECT_MAC_CONN_CLOSED,
|
||||
DECT_MAC_CONN_OPEN_PENDING,
|
||||
DECT_MAC_CONN_OPEN,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_mac_conn - DECT MAC connection as viewed by the DLC
|
||||
*
|
||||
* @list: Cluster connection list node
|
||||
* @cl: Cluster
|
||||
* @mcei: MAC Connection Endpoint Identification
|
||||
* @mci: MAC Connection Identifier (BMCI or AMCI)
|
||||
* @state: Connection state
|
||||
* @service: Service offered by the connection
|
||||
*/
|
||||
struct dect_mac_conn {
|
||||
struct list_head list;
|
||||
struct dect_cluster *cl;
|
||||
|
||||
u32 mcei;
|
||||
struct dect_mci mci;
|
||||
enum dect_mac_conn_states state;
|
||||
enum dect_mac_service_types service;
|
||||
|
||||
struct dect_lc *lc;
|
||||
struct dect_fbx *fbx;
|
||||
};
|
||||
|
||||
extern struct dect_mac_conn *dect_mac_conn_init(struct dect_cluster *cl,
|
||||
const struct dect_mci *mci,
|
||||
const struct dect_mbc_id *id);
|
||||
extern struct dect_mac_conn *dect_mac_conn_get_by_mci(const struct dect_cluster *cl,
|
||||
const struct dect_mci *mci);
|
||||
extern int dect_dlc_mac_conn_establish(struct dect_mac_conn *mc);
|
||||
extern void dect_dlc_mac_conn_release(struct dect_mac_conn *mc);
|
||||
|
||||
extern int dect_dlc_mac_conn_confirm(struct dect_cluster *cl, u32 mcei,
|
||||
enum dect_mac_service_types service);
|
||||
|
||||
extern int dect_dlc_mac_conn_indicate(struct dect_cluster *cl,
|
||||
const struct dect_mbc_id *id);
|
||||
extern int dect_dlc_mac_conn_disconnect(struct dect_cluster *cl, u32 mcei);
|
||||
|
||||
extern void dect_cplane_notify_state_change(struct dect_mac_conn *mc);
|
||||
extern void dect_cplane_rcv(struct dect_mac_conn *mc,
|
||||
enum dect_data_channels chan,
|
||||
struct sk_buff *skb);
|
||||
extern struct sk_buff *dect_cplane_dtr(struct dect_mac_conn *mc,
|
||||
enum dect_data_channels chan);
|
||||
|
||||
extern void dect_uplane_rcv(struct dect_mac_conn *mc,
|
||||
enum dect_data_channels chan,
|
||||
struct sk_buff *skb);
|
||||
extern struct sk_buff *dect_uplane_dtr(struct dect_mac_conn *mc,
|
||||
enum dect_data_channels chan);
|
||||
|
||||
extern void dect_dlc_mac_co_data_indicate(struct dect_cluster *cl, u32 mcei,
|
||||
enum dect_data_channels chan,
|
||||
struct sk_buff *skb);
|
||||
extern struct sk_buff *dect_dlc_mac_co_dtr_indicate(struct dect_cluster *cl, u32 mcei,
|
||||
enum dect_data_channels chan);
|
||||
|
||||
extern void dect_bsap_rcv(const struct dect_cluster *cl, struct sk_buff *skb);
|
||||
extern void dect_dlc_mac_page_indicate(struct dect_cluster *cl,
|
||||
struct sk_buff *skb);
|
||||
|
||||
#endif /* _NET_DECT_DLC_H */
|
|
@ -0,0 +1,223 @@
|
|||
#ifndef _NET_DECT_IDENTITIES_H
|
||||
#define _NET_DECT_IDENTITIES_H
|
||||
|
||||
/*
|
||||
* Acess Rights Identity (ARI)
|
||||
*/
|
||||
|
||||
#define DECT_ARI_ARC_MASK 0xe000000000000000ULL
|
||||
#define DECT_ARI_ARC_SHIFT 61
|
||||
|
||||
/* Class A */
|
||||
#define DECT_ARI_A_EMC_MASK 0x1fffe00000000000ULL
|
||||
#define DECT_ARI_A_EMC_SHIFT 45
|
||||
|
||||
#define DECT_ARI_A_FPN_MASK 0x00001ffff0000000ULL
|
||||
#define DECT_ARI_A_FPN_SHIFT 28
|
||||
|
||||
/* Class B */
|
||||
#define DECT_ARI_B_EIC_MASK 0x1fffe00000000000ULL
|
||||
#define DECT_ARI_B_EIC_SHIFT 45
|
||||
|
||||
#define DECT_ARI_B_FPN_MASK 0x00001fe000000000ULL
|
||||
#define DECT_ARI_B_FPN_SHIFT 37
|
||||
|
||||
#define DECT_ARI_B_FPS_MASK 0x0000001e00000000ULL
|
||||
#define DECT_ARI_B_FPS_SHIFT 33
|
||||
|
||||
/* Class C */
|
||||
#define DECT_ARI_C_POC_MASK 0x1fffe00000000000ULL
|
||||
#define DECT_ARI_C_POC_SHIFT 45
|
||||
|
||||
#define DECT_ARI_C_FPN_MASK 0x00001fe000000000ULL
|
||||
#define DECT_ARI_C_FPN_SHIFT 37
|
||||
|
||||
#define DECT_ARI_C_FPS_MASK 0x0000001e00000000ULL
|
||||
#define DECT_ARI_C_FPS_SHIFT 33
|
||||
|
||||
/* Class D */
|
||||
#define DECT_ARI_D_GOP_MASK 0x1ffffe0000000000ULL
|
||||
#define DECT_ARI_D_GOP_SHIFT 41
|
||||
|
||||
#define DECT_ARI_D_FPN_MASK 0x000001fe00000000ULL
|
||||
#define DECT_ARI_D_FPN_SHIFT 33
|
||||
|
||||
/* Class E */
|
||||
#define DECT_ARI_E_FIL_MASK 0x1fffe00000000000ULL
|
||||
#define DECT_ARI_E_FIL_SHIFT 45
|
||||
|
||||
#define DECT_ARI_E_FPN_MASK 0x00001ffe00000000ULL
|
||||
#define DECT_ARI_E_FPN_SHIFT 33
|
||||
|
||||
#include <linux/dect_netlink.h>
|
||||
|
||||
struct dect_ari {
|
||||
enum dect_ari_classes arc;
|
||||
u32 fpn;
|
||||
u32 fps;
|
||||
union {
|
||||
u16 emc;
|
||||
u16 eic;
|
||||
u16 poc;
|
||||
u32 gop;
|
||||
u16 fil;
|
||||
};
|
||||
};
|
||||
|
||||
enum dect_ari_lengths {
|
||||
DECT_ARC_A_LEN = 36,
|
||||
DECT_ARC_B_LEN = 31,
|
||||
DECT_ARC_C_LEN = 31,
|
||||
DECT_ARC_D_LEN = 31,
|
||||
DECT_ARC_E_LEN = 31,
|
||||
};
|
||||
|
||||
extern bool dect_ari_masked_cmp(const struct dect_ari *a1,
|
||||
const struct dect_ari *a2,
|
||||
const struct dect_ari *m);
|
||||
extern bool dect_ari_cmp(const struct dect_ari *a1, const struct dect_ari *a2);
|
||||
extern u8 dect_parse_ari(struct dect_ari *ari, u64 a);
|
||||
extern u64 dect_build_ari(const struct dect_ari *ari);
|
||||
|
||||
/*
|
||||
* RFPI
|
||||
*/
|
||||
|
||||
#define DECT_RFPI_E_FLAG 0x8000000000000000ULL
|
||||
#define DECT_RFPI_ARI_SHIFT 1
|
||||
#define DECT_RFPI_RPN_SHIFT 24
|
||||
|
||||
struct dect_idi;
|
||||
extern bool dect_rfpi_cmp(const struct dect_idi *i1, const struct dect_idi *i2);
|
||||
extern u64 dect_build_rfpi(const struct dect_idi *idi);
|
||||
|
||||
/*
|
||||
* FMID (Fixed MAC Identifier)
|
||||
*/
|
||||
|
||||
#define DECT_FMID_MASK 0x0fff
|
||||
#define DECT_FMID_SIZE 12
|
||||
|
||||
extern u16 dect_build_fmid(const struct dect_idi *idi);
|
||||
|
||||
/*
|
||||
* PMID (Portable MAC Identifier)
|
||||
*/
|
||||
|
||||
#define DECT_PMID_MASK 0x000fffff
|
||||
#define DECT_PMID_SIZE 20
|
||||
|
||||
#define DECT_PMID_DEFAULT_ID_MASK 0x000f0000
|
||||
#define DECT_PMID_DEFAULT_ID 0x000e0000
|
||||
#define DECT_PMID_DEFAULT_NUM_MASK 0x0000ffff
|
||||
|
||||
#define DECT_PMID_EMERGENCY_ID_MASK 0x000ff000
|
||||
#define DECT_PMID_EMERGENCY_ID 0x000f1000
|
||||
#define DECT_PMID_EMERGENCY_TPUI_MASK 0x00000fff
|
||||
|
||||
#define DECT_PMID_ASSIGNED_TPUI_MASK 0x000fffff
|
||||
|
||||
/**
|
||||
* @DECT_PMID_DEFAULT: 1110 + arbitrary number (16 bits)
|
||||
* @DECT_PMID_ASSIGNED: Assigned individual TPUI
|
||||
* @DECT_PMID_EMERGENCY: 1111 0001 + 12 bits of emergency TPUI
|
||||
*/
|
||||
enum dect_pmid_types {
|
||||
DECT_PMID_DEFAULT,
|
||||
DECT_PMID_ASSIGNED,
|
||||
DECT_PMID_EMERGENCY,
|
||||
};
|
||||
|
||||
struct dect_pmid {
|
||||
enum dect_pmid_types type;
|
||||
union {
|
||||
u32 tpui;
|
||||
u32 num;
|
||||
};
|
||||
};
|
||||
|
||||
extern void dect_parse_pmid(struct dect_pmid *pmid, u32 p);
|
||||
extern u32 dect_build_pmid(const struct dect_pmid *pmid);
|
||||
extern bool dect_pmid_cmp(const struct dect_pmid *p1, const struct dect_pmid *p2);
|
||||
|
||||
/*
|
||||
* ECN (Exchanged Connection Number)
|
||||
*/
|
||||
|
||||
#define DECT_ECN_MASK 0xf
|
||||
#define DECT_ECN_SIZE 4
|
||||
|
||||
/*
|
||||
* LCN (Logical Connection Number)
|
||||
*/
|
||||
|
||||
#define DECT_LCN_MASK 0x7
|
||||
#define DECT_LCN_SIZE 3
|
||||
|
||||
/**
|
||||
* struct dect_mci - MAC connection identifier
|
||||
*
|
||||
* @ari: DECT ARI
|
||||
* @pmid: Portable MAC Identity
|
||||
* @lcn: Logical Connection Number
|
||||
*/
|
||||
struct dect_mci {
|
||||
struct dect_ari ari;
|
||||
struct dect_pmid pmid;
|
||||
u8 lcn;
|
||||
};
|
||||
|
||||
extern int dect_parse_mci(struct dect_mci *mci, u64 m);
|
||||
extern u64 dect_build_mci(const struct dect_mci *mci);
|
||||
|
||||
/*
|
||||
* Data Link Identifier
|
||||
*/
|
||||
|
||||
/**
|
||||
* enum dect_sapis - S SAP Identifier
|
||||
*
|
||||
* @DECT_SAPI_CO_SIGNALLING: connection oriented signalling
|
||||
* @DECT_SAPI_CL_SIGNALLING: connectionless signalling
|
||||
*/
|
||||
enum dect_sapis {
|
||||
DECT_SAPI_CO_SIGNALLING = 0,
|
||||
DECT_SAPI_CL_SIGNALLING = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
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_MAX
|
||||
};
|
||||
#define DECT_LLN_MAX (__DECT_LLN_MAX - 1)
|
||||
|
||||
/**
|
||||
* struct dect_dlei - DECT Data Link Endpoint Identifier (DLEI)
|
||||
*
|
||||
*/
|
||||
struct dect_dlei {
|
||||
struct dect_mci mci;
|
||||
enum dect_sapis sapi;
|
||||
enum dect_llns lln;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_ulei - DECT U-Plane Link Endpoint Identifier
|
||||
*/
|
||||
struct dect_ulei {
|
||||
struct dect_mci mci;
|
||||
};
|
||||
|
||||
#endif /* _NET_DECT_IDENTITIES_H */
|
|
@ -0,0 +1,698 @@
|
|||
/*
|
||||
* DECT MAC Layer - Header and global definitions
|
||||
*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*/
|
||||
|
||||
#ifndef _NET_DECT_MAC_H
|
||||
#define _NET_DECT_MAC_H
|
||||
|
||||
#include <net/dect/identities.h>
|
||||
|
||||
/*
|
||||
* A-Field
|
||||
*/
|
||||
|
||||
#define DECT_A_FIELD_SIZE 8
|
||||
|
||||
#define DECT_RA_FIELD_SIZE 2
|
||||
#define DECT_RA_FIELD_OFF 6
|
||||
|
||||
/*
|
||||
* Header field
|
||||
*/
|
||||
|
||||
#define DECT_HDR_FIELD_SIZE 1
|
||||
#define DECT_HDR_FIELD_OFF 0
|
||||
|
||||
#define DECT_HDR_TA_OFF 0
|
||||
#define DECT_HDR_TA_MASK 0xe0
|
||||
#define DECT_HDR_TA_SHIFT 5
|
||||
|
||||
#define DECT_HDR_Q1_OFF 0
|
||||
#define DECT_HDR_Q1_FLAG 0x10
|
||||
|
||||
#define DECT_HDR_BA_OFF 0
|
||||
#define DECT_HDR_BA_MASK 0x0e
|
||||
#define DECT_HDR_BA_SHIFT 1
|
||||
|
||||
#define DECT_HDR_Q2_OFF 0
|
||||
#define DECT_HDR_Q2_FLAG 0x01
|
||||
|
||||
/*
|
||||
* T-Field
|
||||
*/
|
||||
|
||||
#define DECT_T_FIELD_OFF 1
|
||||
#define DECT_T_FIELD_SIZE 5
|
||||
|
||||
/**
|
||||
* dect_tail_identification - MAC layer T-Field identification
|
||||
*
|
||||
* @DECT_TI_CT_PKT_0: C_T data packet number 0
|
||||
* @DECT_TI_CT_PKT_1: C_T data packet number 1
|
||||
* @DECT_TI_NT_CL: Identities information on connectionless bearer
|
||||
* @DECT_TI_NT: Identities information
|
||||
* @DECT_TI_QT: Multiframe synchronisation und system information
|
||||
* @DECT_TI_RESERVED: Reserved
|
||||
* @DECT_TI_MT: MAC layer control
|
||||
* @DECT_TI_PT: Paging tail (RFP only)
|
||||
* @DECT_TI_MT_PKT_0: MAC layer control (first PP transmission, PP only)
|
||||
*/
|
||||
enum dect_tail_identifications {
|
||||
DECT_TI_CT_PKT_0 = 0x0 << DECT_HDR_TA_SHIFT,
|
||||
DECT_TI_CT_PKT_1 = 0x1 << DECT_HDR_TA_SHIFT,
|
||||
DECT_TI_NT_CL = 0x2 << DECT_HDR_TA_SHIFT,
|
||||
DECT_TI_NT = 0x3 << DECT_HDR_TA_SHIFT,
|
||||
DECT_TI_QT = 0x4 << DECT_HDR_TA_SHIFT,
|
||||
DECT_TI_RESERVED = 0x5 << DECT_HDR_TA_SHIFT,
|
||||
DECT_TI_MT = 0x6 << DECT_HDR_TA_SHIFT,
|
||||
DECT_TI_PT = 0x7 << DECT_HDR_TA_SHIFT,
|
||||
DECT_TI_MT_PKT_0 = 0x7 << DECT_HDR_TA_SHIFT,
|
||||
};
|
||||
|
||||
struct dect_skb_a_cb {
|
||||
enum dect_tail_identifications id;
|
||||
};
|
||||
|
||||
#define DECT_A_CB(skb) ((struct dect_skb_a_cb *)(skb)->cb)
|
||||
|
||||
/*
|
||||
* Identities channel (N-channel)
|
||||
*/
|
||||
|
||||
/* Identities information */
|
||||
#define DECT_NT_ID_RFPI_LEN 5
|
||||
|
||||
/**
|
||||
* @e: indicates whether SARIs are available
|
||||
* @pari: primary access rights identifier
|
||||
* @rpn: radio part number
|
||||
*/
|
||||
struct dect_idi {
|
||||
bool e;
|
||||
struct dect_ari pari;
|
||||
u8 rpn;
|
||||
};
|
||||
|
||||
/*
|
||||
* System information and multiframe marker (Q-channel)
|
||||
*/
|
||||
|
||||
/* RFP Q-channel T-MUX rules: only frame 8 */
|
||||
#define DECT_Q_CHANNEL_FRAME 8
|
||||
|
||||
/* System information header */
|
||||
#define DECT_QT_H_MASK 0xf000000000000000ULL
|
||||
#define DECT_QT_H_SHIFT 60
|
||||
|
||||
/**
|
||||
* dect_system_information_types - codes for system information messages
|
||||
*
|
||||
* @DECT_QT_SI_SSI: static system information
|
||||
* @DECT_QT_SI_ERFC: extended RF carriers
|
||||
* @DECT_QT_SI_FPC: fixed part capabilities
|
||||
* @DECT_QT_SI_EFPC: extended fixed part capabilities
|
||||
* @DECT_QT_SI_SARI: SARI list contents
|
||||
* @DECT_QT_SI_MFN: multi-frame number
|
||||
* @DECT_QT_SI_ESC: escape
|
||||
* @DECT_QT_SI_ERFC2: extended RF carriers part 2
|
||||
* @DECT_QT_SI_TXI transmit information
|
||||
* @DECT_QT_SI_EFPC2: extended fixed part capabilities part 2
|
||||
*/
|
||||
enum dect_mac_system_information_types {
|
||||
DECT_QT_SI_SSI = 0x0ULL << DECT_QT_H_SHIFT,
|
||||
DECT_QT_SI_SSI2 = 0x1ULL << DECT_QT_H_SHIFT,
|
||||
DECT_QT_SI_ERFC = 0x2ULL << DECT_QT_H_SHIFT,
|
||||
DECT_QT_SI_FPC = 0x3ULL << DECT_QT_H_SHIFT,
|
||||
DECT_QT_SI_EFPC = 0x4ULL << DECT_QT_H_SHIFT,
|
||||
DECT_QT_SI_SARI = 0x5ULL << DECT_QT_H_SHIFT,
|
||||
DECT_QT_SI_MFN = 0x6ULL << DECT_QT_H_SHIFT,
|
||||
DECT_QT_SI_ESC = 0x7ULL << DECT_QT_H_SHIFT,
|
||||
DECT_QT_SI_ERFC2 = 0x9ULL << DECT_QT_H_SHIFT,
|
||||
DECT_QT_SI_TXI = 0x10ULL << DECT_QT_H_SHIFT,
|
||||
DECT_QT_SI_EFPC2 = 0x11ULL << DECT_QT_H_SHIFT,
|
||||
};
|
||||
|
||||
/*
|
||||
* Static system information - repeated every 8 multiframes
|
||||
*/
|
||||
|
||||
#define DECT_QT_SSI_FREQ 8
|
||||
|
||||
/* normal reverse */
|
||||
#define DECT_QT_SSI_NR_FLAG 0x1000000000000000ULL
|
||||
|
||||
/* slot number */
|
||||
#define DECT_QT_SSI_SN_MASK 0x0f00000000000000ULL
|
||||
#define DECT_QT_SSI_SN_SHIFT 56
|
||||
|
||||
/* start position */
|
||||
#define DECT_QT_SSI_SP_MASK 0x00c0000000000000ULL
|
||||
#define DECT_QT_SSI_SP_SHIFT 54
|
||||
|
||||
/* escape bit */
|
||||
#define DECT_QT_SSI_ESC_FLAG 0x0020000000000000ULL
|
||||
|
||||
/* number of transceivers */
|
||||
#define DECT_QT_SSI_TXS_MASK 0x0018000000000000ULL
|
||||
#define DECT_QT_SSI_TXS_SHIFT 51
|
||||
|
||||
/* extended RF carrier information available */
|
||||
#define DECT_QT_SSI_MC_FLAG 0x0004000000000000ULL
|
||||
|
||||
/* RF carriers available */
|
||||
#define DECT_QT_SSI_RFCARS_MASK 0x0003ff0000000000ULL
|
||||
#define DECT_QT_SSI_RFCARS_SHIFT 40
|
||||
|
||||
/* carrier number */
|
||||
#define DECT_QT_SSI_CN_MASK 0x0000003f00000000ULL
|
||||
#define DECT_QT_SSI_CN_SHIFT 32
|
||||
|
||||
/* primary scan carrier number */
|
||||
#define DECT_QT_SSI_PSCN_MASK 0x000000003f000000ULL
|
||||
#define DECT_QT_SSI_PSCN_SHIFT 24
|
||||
|
||||
struct dect_ssi {
|
||||
bool nr;
|
||||
bool mc;
|
||||
u16 rfcars;
|
||||
u8 sn;
|
||||
u8 sp;
|
||||
u8 txs;
|
||||
u8 cn;
|
||||
u8 pscn;
|
||||
};
|
||||
|
||||
/*
|
||||
* Extended RF carrier information
|
||||
*/
|
||||
|
||||
#define DECT_QT_ERFC_FREQ 8
|
||||
|
||||
#define DECT_QT_ERFC_RFCARS_MASK 0x0fffffe000000000ULL
|
||||
#define DECT_QT_ERFC_RFCARS_SHIFT 9
|
||||
|
||||
#define DECT_QT_ERFC_RFBAND_MASK 0x0000001f00000000ULL
|
||||
#define DECT_QT_ERFC_RFBAND_SHIFT 32
|
||||
|
||||
#define DECT_QT_ERFC_ERFC2_FLAG 0x0000000080000000ULL
|
||||
|
||||
#define DECT_QT_ERFC_NUM_RFCARS_MASK 0x000000003f000000ULL
|
||||
#define DECT_QT_ERFC_NUM_RFCARS_SHIFT 24
|
||||
|
||||
struct dect_erfc {
|
||||
u32 rfcars;
|
||||
u8 band;
|
||||
u8 num_rfcars;
|
||||
bool erfc2;
|
||||
};
|
||||
|
||||
/*
|
||||
* Fixed Part capabilities
|
||||
*/
|
||||
|
||||
#define DECT_QT_FPC_FREQ 8
|
||||
|
||||
#define DECT_QT_FPC_CAPABILITY_MASK 0x0fffff0000000000ULL
|
||||
#define DECT_QT_FPC_CAPABILITY_SHIFT 40
|
||||
|
||||
#define DECT_QT_FPC_HLC_MASK 0x000000ffff000000ULL
|
||||
#define DECT_QT_FPC_HLC_SHIFT 24
|
||||
|
||||
struct dect_fpc {
|
||||
u32 fpc;
|
||||
u16 hlc;
|
||||
};
|
||||
|
||||
/*
|
||||
* Extended Fixed Part capabilities
|
||||
*/
|
||||
|
||||
#define DECT_QT_EFPC_CRFP_HOPS_MASK 0x0c00000000000000ULL
|
||||
#define DECT_QT_EFPC_CRFP_HOPS_SHIFT 58
|
||||
|
||||
#define DECT_QT_EFPC_CRFP_ENC_FLAG 0x0200000000000000ULL
|
||||
|
||||
#define DECT_QT_EFPC_REP_HOPS_MASK 0x0180000000000000ULL
|
||||
#define DECT_QT_EFPC_REP_HOPS_SHIFT 55
|
||||
|
||||
#define DECT_QT_EFPC_REP_INTERLACE_FLAG 0x0040000000000000ULL
|
||||
|
||||
#define DECT_QT_EFPC_EHLC_MASK 0x000000017f000000ULL
|
||||
#define DECT_QT_EFPC_EHLC_SHIFT 24
|
||||
|
||||
struct dect_efpc {
|
||||
u8 crfp;
|
||||
bool crfp_enc;
|
||||
u8 rep;
|
||||
bool rep_il;
|
||||
u16 ehlc;
|
||||
};
|
||||
|
||||
/*
|
||||
* SARI message
|
||||
*/
|
||||
|
||||
#define DECT_QT_SARI_FREQ 4
|
||||
|
||||
#define DECT_QT_SARI_LIST_CYCLE_MASK 0x000e000000000000ULL
|
||||
#define DECT_QT_SARI_LIST_CYCLE_SHIFT 49
|
||||
|
||||
#define DECT_QT_SARI_TARI_FLAG 0x0001000000000000ULL
|
||||
|
||||
#define DECT_QT_SARI_BLACK_FLAG 0x0000800000000000ULL
|
||||
|
||||
#define DECT_QT_SARI_ARI_MASK 0x00007fffffff0000ULL
|
||||
#define DECT_QT_SARI_ARI_SHIFT 17
|
||||
|
||||
struct dect_sari {
|
||||
u8 list_cycle;
|
||||
bool tari;
|
||||
bool black;
|
||||
struct dect_ari ari;
|
||||
};
|
||||
|
||||
#define DECT_SARI_CYCLE_MAX 16
|
||||
|
||||
/*
|
||||
* Multiframe number - repeated every 8 multiframes if supported
|
||||
*/
|
||||
|
||||
#define DECT_QT_MFN_FREQ 8
|
||||
|
||||
#define DECT_QT_MFN_MASK 0x0000ffffff000000ULL
|
||||
#define DECT_QT_MFN_SHIFT 24
|
||||
|
||||
struct dect_mfn {
|
||||
u32 num;
|
||||
};
|
||||
|
||||
/*
|
||||
* Extended RF carrier information part 2
|
||||
*/
|
||||
|
||||
#define DECT_QT_TXI_ERFC2_FREQ 8
|
||||
|
||||
#define DECT_QT_ERFC2_RFCARS_MASK 0x0fffffffe0000000ULL
|
||||
#define DECT_QT_ERFC2_RFCARS_SHIFT 29
|
||||
|
||||
struct dect_erfc2 {
|
||||
u32 rfcars;
|
||||
};
|
||||
|
||||
/*
|
||||
* Transmit Information
|
||||
*/
|
||||
|
||||
#define DECT_QT_TXI_FREQ 8
|
||||
|
||||
#define DECT_QT_TXI_TYPE_MASK 0x0f00000000000000ULL
|
||||
#define DECT_QT_TXI_TYPE_SHIFT 56
|
||||
|
||||
#define DECT_QT_TXI_PWL_MASK 0x00ff000000000000ULL
|
||||
#define DECT_QT_TXI_PWL_SHIFT 48
|
||||
|
||||
/*
|
||||
* Extended fixed part capabilitiees part 2
|
||||
*/
|
||||
|
||||
/*
|
||||
* Paging Tail (P-channel)
|
||||
*/
|
||||
|
||||
#define DECT_PT_HDR_EXTEND_FLAG 0x8000000000000000ULL
|
||||
|
||||
#define DECT_PT_HDR_LENGTH_MASK 0x7000000000000000ULL
|
||||
#define DECT_PT_HDR_LENGTH_SHIFT 60
|
||||
|
||||
/**
|
||||
* @DECT_PT_ZERO_PAGE: zero length page
|
||||
* @DECT_PT_SHORT_PAGE: short page
|
||||
* @DECT_PT_FULL_PAGE: full page
|
||||
* @DECT_PT_MAX_RESUME_PAGE: MAC resume and control page
|
||||
* @DECT_PT_LONG_PAGE: not the last 36 bits of a long page
|
||||
* @DECT_PT_LONG_PAGE_FIRST: the first 36 bits of a long page
|
||||
* @DECT_PT_LONG_PAGE_LAST: the last 36 bits of a long page
|
||||
* @DECT_PT_LONG_PAGE_ALL: all of a long page (first and last)
|
||||
*
|
||||
*/
|
||||
enum dect_page_lengths {
|
||||
DECT_PT_ZERO_PAGE = 0x0ULL << DECT_PT_HDR_LENGTH_SHIFT,
|
||||
DECT_PT_SHORT_PAGE = 0x1ULL << DECT_PT_HDR_LENGTH_SHIFT,
|
||||
DECT_PT_FULL_PAGE = 0x2ULL << DECT_PT_HDR_LENGTH_SHIFT,
|
||||
DECT_PT_RESUME_PAGE = 0x3ULL << DECT_PT_HDR_LENGTH_SHIFT,
|
||||
DECT_PT_LONG_PAGE = 0x4ULL << DECT_PT_HDR_LENGTH_SHIFT,
|
||||
DECT_PT_LONG_PAGE_FIRST = 0x5ULL << DECT_PT_HDR_LENGTH_SHIFT,
|
||||
DECT_PT_LONG_PAGE_LAST = 0x6ULL << DECT_PT_HDR_LENGTH_SHIFT,
|
||||
DECT_PT_LONG_PAGE_ALL = 0x7ULL << DECT_PT_HDR_LENGTH_SHIFT,
|
||||
};
|
||||
|
||||
/* zero and short page B_S channel data */
|
||||
#define DECT_PT_SZP_BS_DATA_MASK 0x0fffff0000000000ULL
|
||||
#define DECT_PT_SZP_BS_DATA_SHIFT 40
|
||||
#define DECT_PT_SZP_BS_DATA_SIZE 3
|
||||
|
||||
/* long and full page B_S channel data */
|
||||
#define DECT_PT_LFP_BS_DATA_MASK 0x0fffffffff000000ULL
|
||||
#define DECT_PT_LFP_BS_DATA_SHIFT 24
|
||||
#define DECT_PT_LFP_BS_DATA_SIZE 5
|
||||
|
||||
struct dect_page {
|
||||
bool extend;
|
||||
enum dect_page_lengths length;
|
||||
};
|
||||
|
||||
/* MAC layer information */
|
||||
#define DECT_PT_INFO_TYPE_MASK 0x000000f000000000ULL
|
||||
#define DECT_PT_INFO_TYPE_SHIFT 36
|
||||
#define DECT_PT_INFO_TYPE_SIZE 2
|
||||
|
||||
/**
|
||||
* @DECT_PT_IT_FILL_BITS_OR_BLIND_LONG_SLOTS: fill bits/blind long slots if bit 47 set
|
||||
* @DECT_PT_IT_BLIND_FULL_SLOT: blind full slot information
|
||||
* @DECT_PT_IT_OTHER_BEARER:
|
||||
* @DECT_PT_IT_RECOMMENDED_OTHER_BEARER:
|
||||
* @DECT_PT_IT_GOOD_RFP_BEARER:
|
||||
* @DECT_PT_IT_DUMMY_OR_CL_BEARER_POSITION:
|
||||
* @DECT_PT_IT_RFP_IDENTITY:
|
||||
* @DECT_PT_IT_ESCAPE:
|
||||
* @DECT_PT_IT_DUMMY_OR_CL_BEARER_MARKER:
|
||||
* @DECT_PT_IT_BEARER_HANDOVER_INFO:
|
||||
* @DECT_PT_IT_RFP_STATUS:
|
||||
* @DECT_PT_IT_ACTIVE_CARRIERS:
|
||||
* @DECT_PT_IT_CL_BEARER_POSITION:
|
||||
* @DECT_PT_IT_RECOMMENDED_POWER_LEVEL:
|
||||
* @DECT_PT_IT_BLIND_DOUBLE_SLOT:
|
||||
* @DECT_PT_IT_BLIND_FULL_SLOT_PACKET_MODE:
|
||||
*
|
||||
*/
|
||||
enum dect_pt_info_types {
|
||||
DECT_PT_IT_FILL_BITS_OR_BLIND_LONG_SLOTS= 0x0ULL << DECT_PT_INFO_TYPE_SHIFT,
|
||||
DECT_PT_IT_BLIND_FULL_SLOT = 0x1ULL << DECT_PT_INFO_TYPE_SHIFT,
|
||||
DECT_PT_IT_OTHER_BEARER = 0x2ULL << DECT_PT_INFO_TYPE_SHIFT,
|
||||
DECT_PT_IT_RECOMMENDED_OTHER_BEARER = 0x3ULL << DECT_PT_INFO_TYPE_SHIFT,
|
||||
DECT_PT_IT_GOOD_RFP_BEARER = 0x4ULL << DECT_PT_INFO_TYPE_SHIFT,
|
||||
DECT_PT_IT_DUMMY_OR_CL_BEARER_POSITION = 0x5ULL << DECT_PT_INFO_TYPE_SHIFT,
|
||||
DECT_PT_IT_RFP_IDENTITY = 0x6ULL << DECT_PT_INFO_TYPE_SHIFT,
|
||||
DECT_PT_IT_ESCAPE = 0x7ULL << DECT_PT_INFO_TYPE_SHIFT,
|
||||
DECT_PT_IT_DUMMY_OR_CL_BEARER_MARKER = 0x8ULL << DECT_PT_INFO_TYPE_SHIFT,
|
||||
DECT_PT_IT_BEARER_HANDOVER_INFO = 0x9ULL << DECT_PT_INFO_TYPE_SHIFT,
|
||||
DECT_PT_IT_RFP_STATUS = 0xaULL << DECT_PT_INFO_TYPE_SHIFT,
|
||||
DECT_PT_IT_ACTIVE_CARRIERS = 0xbULL << DECT_PT_INFO_TYPE_SHIFT,
|
||||
DECT_PT_IT_CL_BEARER_POSITION = 0xcULL << DECT_PT_INFO_TYPE_SHIFT,
|
||||
DECT_PT_IT_RECOMMENDED_POWER_LEVEL = 0xdULL << DECT_PT_INFO_TYPE_SHIFT,
|
||||
DECT_PT_IT_BLIND_DOUBLE_SLOT = 0xeULL << DECT_PT_INFO_TYPE_SHIFT,
|
||||
DECT_PT_IT_BLIND_FULL_SLOT_PACKET_MODE = 0xfULL << DECT_PT_INFO_TYPE_SHIFT,
|
||||
};
|
||||
|
||||
/* blind full slot information */
|
||||
#define DECT_PT_BFS_MASK 0x0000000fff000000ULL
|
||||
#define DECT_PT_BFS_SHIFT 24
|
||||
|
||||
struct dect_bfs {
|
||||
struct dect_page page;
|
||||
u16 mask;
|
||||
};
|
||||
|
||||
/* Bearer description */
|
||||
#define DECT_PT_BEARER_SN_MASK 0x0000000f00000000ULL
|
||||
#define DECT_PT_BEARER_SN_SHIFT 32
|
||||
|
||||
#define DECT_PT_BEARER_SP_MASK 0x00000000c0000000ULL
|
||||
#define DECT_PT_BEARER_SP_SHIFT 30
|
||||
|
||||
#define DECT_PT_BEARER_CN_MASK 0x000000003f000000ULL
|
||||
#define DECT_PT_BEARER_CN_SHIFT 24
|
||||
|
||||
struct dect_bearer_desc {
|
||||
struct dect_page page;
|
||||
enum dect_pt_info_types bt;
|
||||
u8 sn;
|
||||
u8 sp;
|
||||
u8 cn;
|
||||
};
|
||||
|
||||
/* RFP identity */
|
||||
#define DECT_PT_RFP_ID_MASK 0x0000000fff000000ULL
|
||||
#define DECT_PT_RFP_ID_SHIFT 24
|
||||
|
||||
struct dect_rfp_id {
|
||||
struct dect_page page;
|
||||
u16 id;
|
||||
};
|
||||
|
||||
/* RFP status */
|
||||
#define DECT_PT_RFPS_RFP_BUSY_FLAG 0x0000000100000000ULL
|
||||
#define DECT_PT_RFPS_SYS_BUSY_FLAG 0x0000000200000000ULL
|
||||
|
||||
struct dect_rfp_status {
|
||||
struct dect_page page;
|
||||
bool rfp_busy;
|
||||
bool sys_busy;
|
||||
};
|
||||
|
||||
/* Active carriers */
|
||||
#define DECT_PT_ACTIVE_CARRIERS_MASK 0x0000000ffc000000ULL
|
||||
#define DECT_PT_ACTIVE_CARRIERS_SHIFT 26
|
||||
|
||||
struct dect_active_carriers {
|
||||
struct dect_page page;
|
||||
u16 active;
|
||||
};
|
||||
|
||||
/*
|
||||
* MAC control (M-channel)
|
||||
*/
|
||||
|
||||
#define DECT_MT_FRAME_RATE 2
|
||||
|
||||
#define DECT_MT_HDR_MASK 0xf000000000000000ULL
|
||||
#define DECT_MT_HDR_SHIFT 60
|
||||
|
||||
#define DECT_MT_CMD_MASK 0x0f00000000000000ULL
|
||||
#define DECT_MT_CMD_SHIFT 56
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
enum dect_mt_hdr_type {
|
||||
DECT_MT_BASIC_CCTRL = 0x0ULL << DECT_MT_HDR_SHIFT,
|
||||
DECT_MT_ADV_CCTRL = 0x1ULL << DECT_MT_HDR_SHIFT,
|
||||
DECT_MT_MAC_TEST = 0x2ULL << DECT_MT_HDR_SHIFT,
|
||||
DECT_MT_QUALITY_CTRL = 0x3ULL << DECT_MT_HDR_SHIFT,
|
||||
DECT_MT_BRD_CL_SERVICE = 0x4ULL << DECT_MT_HDR_SHIFT,
|
||||
DECT_MT_ENC_CTRL = 0x5ULL << DECT_MT_HDR_SHIFT,
|
||||
DECT_MT_XYZ = 0x6ULL << DECT_MT_HDR_SHIFT,
|
||||
DECT_MT_ESC = 0x7ULL << DECT_MT_HDR_SHIFT,
|
||||
DECT_MT_TARI = 0x8ULL << DECT_MT_HDR_SHIFT,
|
||||
DECT_MT_REP_CCTRL = 0x9ULL << DECT_MT_HDR_SHIFT,
|
||||
};
|
||||
|
||||
/* basic connection control */
|
||||
enum dect_basic_cctrl_cmds {
|
||||
DECT_BASIC_CCTRL_ACCESS_REQ = 0x0ULL << DECT_MT_CMD_SHIFT,
|
||||
DECT_BASIC_CCTRL_BEARER_HO_REQ = 0x1ULL << DECT_MT_CMD_SHIFT,
|
||||
DECT_BASIC_CCTRL_CONNECTION_HO_REQ = 0x2ULL << DECT_MT_CMD_SHIFT,
|
||||
DECT_BASIC_CCTRL_UNCONFIRMED_ACCESS_REQ = 0x3ULL << DECT_MT_CMD_SHIFT,
|
||||
DECT_BASIC_CCTRL_BEARER_CONFIRM = 0x4ULL << DECT_MT_CMD_SHIFT,
|
||||
DECT_BASIC_CCTRL_WAIT = 0x5ULL << DECT_MT_CMD_SHIFT,
|
||||
DECT_BASIC_CCTRL_RELEASE = 0xfULL << DECT_MT_CMD_SHIFT,
|
||||
};
|
||||
|
||||
#define DECT_MT_BCCTRL_FMID_MASK 0x00fff00000000000ULL
|
||||
#define DECT_MT_BCCTRL_FMID_SHIFT 44
|
||||
|
||||
#define DECT_MT_BCCTRL_PMID_MASK 0x00000fffff000000ULL
|
||||
#define DECT_MT_BCCTRL_PMID_SHIFT 24
|
||||
|
||||
struct dect_bcctrl {
|
||||
enum dect_basic_cctrl_cmds cmd;
|
||||
u16 fmid;
|
||||
u32 pmid;
|
||||
};
|
||||
|
||||
/* marker for T-MUX exceptions */
|
||||
#define DECT_MT_HIGH_PRIORITY 0x1
|
||||
|
||||
/*
|
||||
* C_T data
|
||||
*/
|
||||
|
||||
#define DECT_C_S_SDU_SIZE 5
|
||||
|
||||
struct dect_ct_data {
|
||||
bool pkt_0;
|
||||
};
|
||||
|
||||
/*
|
||||
* Flat representation of tail message contents
|
||||
*/
|
||||
enum dect_tail_msg_types {
|
||||
DECT_TM_TYPE_INVALID,
|
||||
DECT_TM_TYPE_ID,
|
||||
DECT_TM_TYPE_SSI,
|
||||
DECT_TM_TYPE_ERFC,
|
||||
DECT_TM_TYPE_FPC,
|
||||
DECT_TM_TYPE_EFPC,
|
||||
DECT_TM_TYPE_SARI,
|
||||
DECT_TM_TYPE_MFN,
|
||||
DECT_TM_TYPE_PAGE,
|
||||
DECT_TM_TYPE_BFS,
|
||||
DECT_TM_TYPE_BD,
|
||||
DECT_TM_TYPE_RFP_ID,
|
||||
DECT_TM_TYPE_RFP_STATUS,
|
||||
DECT_TM_TYPE_ACTIVE_CARRIERS,
|
||||
DECT_TM_TYPE_BCCTRL,
|
||||
DECT_TM_TYPE_CT,
|
||||
};
|
||||
|
||||
struct dect_tail_msg {
|
||||
enum dect_tail_identifications ti;
|
||||
enum dect_tail_msg_types type;
|
||||
union {
|
||||
struct dect_idi idi;
|
||||
struct dect_ssi ssi;
|
||||
struct dect_erfc erfc;
|
||||
struct dect_fpc fpc;
|
||||
struct dect_efpc efpc;
|
||||
struct dect_sari sari;
|
||||
struct dect_mfn mfn;
|
||||
struct dect_page page;
|
||||
struct dect_bfs bfs;
|
||||
struct dect_bearer_desc bd;
|
||||
struct dect_rfp_id rfp_id;
|
||||
struct dect_rfp_status rfp_status;
|
||||
struct dect_active_carriers active_carriers;
|
||||
struct dect_bcctrl bctl;
|
||||
struct dect_ct_data ctd;
|
||||
};
|
||||
};
|
||||
|
||||
struct dect_si {
|
||||
u32 mask;
|
||||
struct dect_ssi ssi;
|
||||
struct dect_erfc erfc;
|
||||
struct dect_fpc fpc;
|
||||
struct dect_efpc efpc;
|
||||
struct dect_sari sari[DECT_SARI_CYCLE_MAX];
|
||||
struct dect_mfn mfn;
|
||||
u8 num_saris;
|
||||
};
|
||||
|
||||
/*
|
||||
* B-Field
|
||||
*/
|
||||
|
||||
#define DECT_B_FIELD_SIZE 40
|
||||
|
||||
/**
|
||||
* dect_b_identitifications - MAC layer B-Field Identification
|
||||
*
|
||||
* @DECT_BI_UTYPE_0: U-Type, I_N, SI_N, SI_P or I_P packet number 0
|
||||
* @DECT_BI_UTYPE_1: U-Type, I_P error detect or I_P packet number 1
|
||||
* @DECT_BI_ETYPE_CF_0: E-Type, all C_F or CL_F, packet number 0
|
||||
* @DECT_BI_ETYPE_CF_1: E-Type, all C_F, packet number 1
|
||||
* @DECT_BI_ETYPE_MAC: E-Type, all MAC control (unnumbered)
|
||||
* @DECT_BI_NONE: no B-Field
|
||||
*/
|
||||
enum dect_b_identifications {
|
||||
DECT_BI_UTYPE_0 = 0x0 << DECT_HDR_BA_SHIFT,
|
||||
DECT_BI_UTYPE_1 = 0x1 << DECT_HDR_BA_SHIFT,
|
||||
DECT_BI_ETYPE_CF_0 = 0x2 << DECT_HDR_BA_SHIFT,
|
||||
DECT_BI_ETYPE_CF_1 = 0x3 << DECT_HDR_BA_SHIFT,
|
||||
DECT_BI_ETYPE_MAC = 0x6 << DECT_HDR_BA_SHIFT,
|
||||
DECT_BI_NONE = 0x7 << DECT_HDR_BA_SHIFT,
|
||||
};
|
||||
|
||||
struct dect_skb_b_cb {
|
||||
enum dect_b_identifications id;
|
||||
};
|
||||
|
||||
#define DECT_B_CB(skb) ((struct dect_skb_b_cb *)(skb)->cb)
|
||||
|
||||
#define DECT_C_F_SDU_SIZE 8
|
||||
#define DECT_G_F_SDU_SIZE 8
|
||||
|
||||
/**
|
||||
* enum dect_mac_channels - internal MAC control channels
|
||||
*
|
||||
* @DECT_MC_Q: System information and multiframe marker
|
||||
* @DECT_MC_N: Identities information
|
||||
* @DECT_MC_M: MAC control channel
|
||||
* @DECT_MC_P: MAC Paging channel
|
||||
*/
|
||||
enum dect_mac_channels {
|
||||
DECT_MC_Q,
|
||||
DECT_MC_N,
|
||||
DECT_MC_M,
|
||||
DECT_MC_P,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum dect_data_channels - logical MAC data channels
|
||||
*
|
||||
* @DECT_MC_G_F:
|
||||
* @DECT_MC_C_S: Higher layer C-Plane channel (slow)
|
||||
* @DECT_MC_C_F: Higher layer C-Plane channel (fast)
|
||||
* @DECT_MC_I_N: Higher layer U-Plane channel (numbered)
|
||||
* @DECT_MC_I_P: Higher layer U-Plane channel (protected)
|
||||
* @DECT_MC_SI_N: Higher layer connectionless U-Plane channel (numbered)
|
||||
* @DECT_MC_SI_P: Higher layer connectionless U-Plane channel (protected)
|
||||
*/
|
||||
enum dect_data_channels {
|
||||
DECT_MC_G_F,
|
||||
DECT_MC_C_S,
|
||||
DECT_MC_C_F,
|
||||
DECT_MC_I_N,
|
||||
DECT_MC_I_P,
|
||||
DECT_MC_SI_N,
|
||||
DECT_MC_SI_P,
|
||||
__DECT_MC_MAX
|
||||
};
|
||||
#define DECT_MC_MAX (__DECT_MC_MAX - 1)
|
||||
|
||||
/**
|
||||
* enum dect_mac_connection_types - MAC Connection types
|
||||
*
|
||||
* @DECT_MAC_CONN_BASIC: Basic connection, always I_N_min_delay service
|
||||
* @DECT_MAC_CONN_ADV: Advanced connection
|
||||
* @DECT_MAC_CONN_COMPLEMENT: Complementary connection
|
||||
*/
|
||||
enum dect_mac_connection_types {
|
||||
DECT_MAC_CONN_BASIC,
|
||||
DECT_MAC_CONN_ADV,
|
||||
DECT_MAC_CONN_COMPLEMENT,
|
||||
};
|
||||
|
||||
enum dect_mac_service_types {
|
||||
DECT_SERVICE_IN_MIN_DELAY,
|
||||
DECT_SERVICE_IN_NORM_DELAY,
|
||||
DECT_SERVICE_IP_ERROR_DETECTION,
|
||||
DECT_SERVICE_IP_ERROR_CORRECTION,
|
||||
DECT_SERVICE_U_PLANE_UNKNOWN,
|
||||
DECT_SERVICE_C_ONLY,
|
||||
DECT_SERVICE_IPQ_ERROR_DETECTION,
|
||||
DECT_SERVICE_IPQ_ERROR_CORRECTION,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_mbc_id
|
||||
*
|
||||
* @mcei: MAC Connection Endpoint Identifier
|
||||
* @type: Connection Type (Basic/Advanced)
|
||||
* @ari: FT identifier
|
||||
* @pmid: Portable MAC Identity
|
||||
* @ecn: Exchanged Connection Number
|
||||
* @service: Service type
|
||||
*/
|
||||
struct dect_mbc_id {
|
||||
u32 mcei;
|
||||
enum dect_mac_connection_types type;
|
||||
struct dect_ari ari;
|
||||
struct dect_pmid pmid;
|
||||
u8 ecn;
|
||||
enum dect_mac_service_types service;
|
||||
};
|
||||
|
||||
#endif /* _NET_DECT_MAC_H */
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* DECT MAC Layer - Cluster Control Functions (CCF)
|
||||
*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*/
|
||||
|
||||
#ifndef _NET_DECT_MAC_CCF_H
|
||||
#define _NET_DECT_MAC_CCF_H
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/dect/mac.h>
|
||||
|
||||
/**
|
||||
* struct dect_bmc_skb_cb
|
||||
*
|
||||
* @fast: fast page
|
||||
* @stamp: multiframe number at time of TX request
|
||||
* @repetitions: number of page repetitions
|
||||
*/
|
||||
struct dect_bmc_skb_cb {
|
||||
bool fast;
|
||||
u32 stamp;
|
||||
u8 repetitions;
|
||||
};
|
||||
#define DECT_BMC_CB(skb) ((struct dect_bmc_skb_cb *)(skb)->cb)
|
||||
|
||||
#define DECT_PAGE_LIFETIME 6 /* multiframes */
|
||||
|
||||
/**
|
||||
* struct dect_bmc - broadcast message control
|
||||
*
|
||||
* @bcs: broadcast controller list
|
||||
* @index: system information round robin index
|
||||
*/
|
||||
struct dect_bmc {
|
||||
struct list_head bcs;
|
||||
unsigned int index;
|
||||
};
|
||||
|
||||
struct dect_cmc {
|
||||
|
||||
};
|
||||
|
||||
enum dect_mbc_state {
|
||||
DECT_MBC_NONE,
|
||||
DECT_MBC_INITIATED,
|
||||
DECT_MBC_ESTABLISHED,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_mbc - DECT Multi-Bearer Control
|
||||
*
|
||||
* @list: Cluster connection list node
|
||||
* @cl: Cluster the MBC is contained in
|
||||
* @type: connection type
|
||||
* @id: MBC identity
|
||||
* @state: MBC state
|
||||
* @timer: Connection setup timer (T200)
|
||||
* @ch: Cell handling associated traffic bearer
|
||||
* @setup_cnt: number of setup attempts (N200)
|
||||
* @c_tx_queue: C-channel TX-queue
|
||||
* @i_tx_queue: I-channel TX-queue
|
||||
* @gf_tx_queue: G_F-channel TX-queue
|
||||
* @cs_rx_seq: C_S receive sequence number
|
||||
* @cs_tx_seq: C_S transmit sequence number
|
||||
*/
|
||||
struct dect_mbc {
|
||||
struct list_head list;
|
||||
struct dect_cluster *cl;
|
||||
|
||||
enum dect_mac_connection_types type;
|
||||
struct dect_mbc_id id;
|
||||
enum dect_mbc_state state;
|
||||
|
||||
struct timer_list timer;
|
||||
const struct dect_cell_handle *ch;
|
||||
u8 setup_cnt;
|
||||
|
||||
struct sk_buff_head c_tx_queue;
|
||||
struct sk_buff_head i_tx_queue;
|
||||
struct sk_buff_head gf_tx_queue;
|
||||
u8 cs_rx_seq;
|
||||
u8 cs_tx_seq;
|
||||
};
|
||||
|
||||
#define DECT_MBC_SETUP_TIMEOUT (5 * HZ) /* seconds */
|
||||
#define DECT_MBC_SETUP_MAX_ATTEMPTS 10
|
||||
|
||||
extern u32 dect_mbc_alloc_mcei(struct dect_cluster *cl);
|
||||
extern int dect_mbc_con_request(struct dect_cluster *cl,
|
||||
const struct dect_mbc_id *id);
|
||||
|
||||
extern void dect_bmc_mac_page_request(struct dect_cluster *cl,
|
||||
struct sk_buff *skb, bool expedited);
|
||||
|
||||
struct dect_llme_req;
|
||||
extern int dect_cluster_scan(struct dect_cluster *cl,
|
||||
const struct dect_llme_req *lreq,
|
||||
const struct dect_ari *pari,
|
||||
const struct dect_ari *pari_mask);
|
||||
|
||||
/**
|
||||
* struct dect_ccf_ops - Cluster Control Ops
|
||||
*
|
||||
* @bind: bind cell to cluster
|
||||
* @unbind: unbind cell from cluster
|
||||
* @mac_info_indicate: indicate FP mac layer information (PP only)
|
||||
* @mbc_conn_indicate: indicate a new TBC connection
|
||||
* @mbc_conn_notify: notify MBC of TBC events
|
||||
* @mbc_data_indicate: indicate new data to MBC
|
||||
* @mbc_dtr_indicate: indicate data transmit ready to MBC
|
||||
* @bmc_page_indicate: indicate reception of a page message to the BMC
|
||||
*/
|
||||
struct dect_cluster_handle;
|
||||
struct dect_scan_result;
|
||||
enum dect_tbc_event;
|
||||
struct dect_ccf_ops {
|
||||
int (*bind)(struct dect_cluster_handle *,
|
||||
struct dect_cell_handle *);
|
||||
void (*unbind)(struct dect_cluster_handle *,
|
||||
struct dect_cell_handle *);
|
||||
|
||||
void (*scan_report)(const struct dect_cluster_handle *,
|
||||
const struct dect_scan_result *);
|
||||
|
||||
void (*mac_info_indicate)(const struct dect_cluster_handle *,
|
||||
const struct dect_idi *,
|
||||
const struct dect_si *);
|
||||
|
||||
int (*mbc_conn_indicate)(const struct dect_cluster_handle *,
|
||||
const struct dect_cell_handle *,
|
||||
const struct dect_mbc_id *);
|
||||
int (*mbc_conn_notify)(const struct dect_cluster_handle *,
|
||||
const struct dect_mbc_id *,
|
||||
enum dect_tbc_event);
|
||||
void (*mbc_data_indicate)(const struct dect_cluster_handle *,
|
||||
const struct dect_mbc_id *,
|
||||
enum dect_data_channels chan,
|
||||
struct sk_buff *);
|
||||
void (*mbc_dtr_indicate)(const struct dect_cluster_handle *,
|
||||
const struct dect_mbc_id *,
|
||||
enum dect_data_channels chan);
|
||||
|
||||
void (*bmc_page_indicate)(const struct dect_cluster_handle *,
|
||||
struct sk_buff *);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_cluster_handle - Cell's view of a cluster
|
||||
*
|
||||
* @ops: Cluster Control Function ops
|
||||
* @index: Cluster index
|
||||
* @tipc_id: Cluster TIPC user ID
|
||||
* @tportref: Topology Service port reference (remote cluster only)
|
||||
* @portref: Cell Control Protocol port reference (remote cluster only)
|
||||
*/
|
||||
struct dect_cluster_handle {
|
||||
const struct dect_ccf_ops *ops;
|
||||
u8 index;
|
||||
|
||||
u32 tipc_id;
|
||||
u32 tportref;
|
||||
u32 portref;
|
||||
};
|
||||
|
||||
#endif /* _NET_DECT_MAC_CCF_H */
|
|
@ -0,0 +1,556 @@
|
|||
/*
|
||||
* DECT MAC Layer - Cell Site Functions (CSF)
|
||||
*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*/
|
||||
|
||||
#ifndef _NET_DECT_MAC_CSF_H
|
||||
#define _NET_DECT_MAC_CSF_H
|
||||
|
||||
#include <net/dect/mac.h>
|
||||
#include <net/dect/transceiver.h>
|
||||
|
||||
/**
|
||||
* enum dect_timer_bases - timer bases for DECT timers
|
||||
*
|
||||
* @DECT_TIMER_RX: receive time base
|
||||
* @DECT_TIMER_TX: send time base
|
||||
*/
|
||||
enum dect_timer_bases {
|
||||
DECT_TIMER_RX,
|
||||
DECT_TIMER_TX,
|
||||
__DECT_TIMER_BASE_MAX
|
||||
};
|
||||
#define DECT_TIMER_BASE_MAX (__DECT_TIMER_BASE_MAX - 1)
|
||||
|
||||
/**
|
||||
* struct dect_timer_base - timer base
|
||||
*
|
||||
* @timers: list of active timers
|
||||
* @slot: slot position
|
||||
* @framenum: frame number
|
||||
* @mfn: multiframe number
|
||||
*/
|
||||
struct dect_timer_base {
|
||||
struct list_head timers;
|
||||
u8 slot;
|
||||
u8 framenum;
|
||||
u32 mfn;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_timer - DECT TDMA frame timer
|
||||
*
|
||||
* @list: timer list node
|
||||
* @base: timer base
|
||||
* @mfn: expiration time: multiframe number
|
||||
* @frame: expiration time: frame number
|
||||
* @slot: expiration time: slot number
|
||||
* @func: timer function
|
||||
* @data: timer data
|
||||
*/
|
||||
struct dect_timer {
|
||||
struct list_head list;
|
||||
|
||||
enum dect_timer_bases base;
|
||||
u32 mfn;
|
||||
u8 frame;
|
||||
u8 slot;
|
||||
|
||||
void (*func)(struct dect_cell *, void *);
|
||||
void *data;
|
||||
};
|
||||
|
||||
#define DECT_CHANNEL_LIST_DBM_RES 6
|
||||
#define DECT_CHANNEL_LIST_BINS (DECT_RSSI_DBM_RANGE / DECT_CHANNEL_LIST_DBM_RES)
|
||||
|
||||
/**
|
||||
* struct dect_channel_list_entry
|
||||
*
|
||||
* @list: channel list bin node
|
||||
* @slot: slot number
|
||||
* @carrier: RF-carrier
|
||||
* @rssi: measured RSSI value
|
||||
*/
|
||||
struct dect_channel_list_entry {
|
||||
struct list_head list;
|
||||
u8 slot;
|
||||
u8 carrier;
|
||||
u8 rssi;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_channel_list - Basic channel list
|
||||
*
|
||||
* @list: cell's channel lists list node
|
||||
* @pkt: packet type used for RSSI measurement
|
||||
* @status: bitmask of completed carriers
|
||||
* @timer: update timer
|
||||
* @available: number of available entries
|
||||
* @bins: channels ordered by RSSI value
|
||||
* @entries: channel list entries
|
||||
*
|
||||
* A channel list contains channel descriptions of all physical channels
|
||||
* able to carry the packet type, sorted into multiple bins based on the
|
||||
* maximum RSSI value of the TDD slot pair.
|
||||
*/
|
||||
struct dect_channel_list {
|
||||
struct list_head list;
|
||||
enum dect_packet_types pkt;
|
||||
u64 status;
|
||||
|
||||
struct dect_timer timer;
|
||||
u16 available;
|
||||
struct list_head bins[DECT_CHANNEL_LIST_BINS];
|
||||
struct dect_channel_list_entry entries[];
|
||||
};
|
||||
|
||||
#define DECT_CHANNEL_LIST_MAX_AGE 30 /* T209: 30 seconds */
|
||||
#define DECT_CHANNEL_LIST_MAX_DBM -50 /* dBm */
|
||||
#define DECT_CHANNEL_LIST_LOW_WATERMARK 20 /* channels */
|
||||
|
||||
/**
|
||||
* enum dect_bearer_types
|
||||
*
|
||||
* @DECT_SIMPLEX_BEARER: simplex bearer, one physical channel
|
||||
* @DECT_DUPLEX_BEARER: two simplex bearers on opposite channels
|
||||
* @DECT_DOUBLE_SIMLEX_BEARER: two simplex bearers in same direction
|
||||
* @DECT_DOUBLE_DUPLEX_BEARER: two duplex bearers
|
||||
*/
|
||||
enum dect_bearer_types {
|
||||
DECT_SIMPLEX_BEARER,
|
||||
DECT_DUPLEX_BEARER,
|
||||
DECT_DOUBLE_SIMPLEX_BEARER,
|
||||
DECT_DOUBLE_DUPLEX_BEARER,
|
||||
};
|
||||
|
||||
enum dect_bearer_states {
|
||||
DECT_DUMMY_BEARER,
|
||||
DECT_TRAFFIC_BEARER,
|
||||
DECT_CL_BEARER,
|
||||
};
|
||||
|
||||
enum dect_bearer_modes {
|
||||
DECT_BEARER_RX,
|
||||
DECT_BEARER_TX,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum dect_bearer_state - DECT MAC bearer states
|
||||
*
|
||||
* @DECT_BEARER_INACTIVE: bearer inactive
|
||||
* @DECT_BEARER_SCHEDULED: bearer is scheduled for activation
|
||||
* @DECT_BEARER_RSSI_CONFIRM: bearer is scheduled for RSSI confirmation
|
||||
* @DECT_BEARER_RSSI_CONFIRMED: RSSI is confirmed, bearer is scheduled for e
|
||||
* @DECT_BEARER_ENABLED: bearer is enabled
|
||||
*/
|
||||
enum dect_bearer_state {
|
||||
DECT_BEARER_INACTIVE,
|
||||
DECT_BEARER_SCHEDULED,
|
||||
DECT_BEARER_RSSI_CONFIRM,
|
||||
DECT_BEARER_RSSI_CONFIRMED,
|
||||
DECT_BEARER_ENABLED,
|
||||
};
|
||||
|
||||
struct dect_bearer;
|
||||
struct dect_bearer_ops {
|
||||
enum dect_bearer_states state;
|
||||
void (*enable)(struct dect_cell *, struct dect_bearer *);
|
||||
void (*report_rssi)(struct dect_cell *, struct dect_bearer *,
|
||||
u8 slot, u8 rssi);
|
||||
void (*rcv)(struct dect_cell *cell, struct dect_bearer *,
|
||||
struct sk_buff *);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_bearer - DECT MAC Bearer
|
||||
*
|
||||
* @type: bearer type
|
||||
* @state: operational state
|
||||
* @trx: DECT transceiver
|
||||
* @chd: channel description
|
||||
* @mode: bearer mode (RX/TX)
|
||||
* @tx_timer: TX enable timer
|
||||
* @rssi: last measured RSSI of selected channel
|
||||
* @m_tx_queue: M-channel TX queue
|
||||
* @q: Hdr-field MUX for Q1/Q2 bit settings
|
||||
* @union: bearer type specific data
|
||||
*/
|
||||
struct dect_bearer {
|
||||
enum dect_bearer_types type;
|
||||
const struct dect_bearer_ops *ops;
|
||||
struct dect_transceiver *trx;
|
||||
struct dect_channel_desc chd;
|
||||
enum dect_bearer_modes mode;
|
||||
enum dect_bearer_state state;
|
||||
struct dect_timer tx_timer;
|
||||
u8 rssi;
|
||||
|
||||
struct sk_buff_head m_tx_queue;
|
||||
u8 q;
|
||||
|
||||
union {
|
||||
struct dect_dbc *dbc;
|
||||
struct dect_cbc *cbc;
|
||||
struct dect_tbc *tbc;
|
||||
struct dect_irc *irc;
|
||||
void *data;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_bc - broadcast controller
|
||||
*
|
||||
* @list: broadcast message control BC list node
|
||||
* @p_rx_skb: current RX P-channel message
|
||||
*/
|
||||
struct dect_bc {
|
||||
struct list_head list;
|
||||
struct sk_buff *p_rx_skb;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_dbc - dummy bearer control
|
||||
*
|
||||
* @list: cell dbc list node
|
||||
* @cell: DECT cell
|
||||
* @bearer: dummy bearer
|
||||
* @bc: broadcast controller
|
||||
*/
|
||||
struct dect_dbc {
|
||||
struct list_head list;
|
||||
struct dect_cell *cell;
|
||||
struct dect_bearer *bearer;
|
||||
struct dect_bc bc;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct dect_cbc - connectionless bearer control
|
||||
*
|
||||
* @cell: DECT cell
|
||||
* @dl_bearer: connectionless downlink bearer
|
||||
* @ul_bearer: connectionless uplink bearer, if present
|
||||
* @bc: broadcast controller
|
||||
*/
|
||||
struct dect_cbc {
|
||||
struct dect_cell *cell;
|
||||
struct dect_bearer *dl_bearer;
|
||||
struct dect_bearer *ul_bearer;
|
||||
struct dect_bc bc;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum dect_tbc_state - DECT Traffic Bearer Controller state
|
||||
*
|
||||
* @DECT_TBC_NONE: Initial state
|
||||
* @DECT_TBC_REQ_SENT: Initiator: bearer request sent
|
||||
* @DECT_TBC_WAIT_RCVD: Initiator: intermediate state
|
||||
* @DECT_TBC_CONFIRM_WAIT: Initiator: waiting for confirmation
|
||||
* @DECT_TBC_REQ_RCVD: Responder: request received
|
||||
* @DECT_TBC_RESPONSE_SENT: Responder: immediate response to request sent
|
||||
* @DECT_TBC_OTHER_WAIT: Waiting for "other" message
|
||||
* @DECT_TBC_ESTABLISHED Established
|
||||
* @DECT_TBC_RELEASING First RELEASE message sent
|
||||
* @DECT_TBC_RELEASED: Second RELEASE message sent
|
||||
*/
|
||||
enum dect_tbc_state {
|
||||
DECT_TBC_NONE,
|
||||
DECT_TBC_REQ_SENT,
|
||||
DECT_TBC_WAIT_RCVD,
|
||||
DECT_TBC_REQ_RCVD,
|
||||
DECT_TBC_RESPONSE_SENT,
|
||||
DECT_TBC_OTHER_WAIT,
|
||||
DECT_TBC_ESTABLISHED,
|
||||
DECT_TBC_RELEASING,
|
||||
DECT_TBC_RELEASED,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum dect_tbc_event - DECT Traffic Bearer events
|
||||
*
|
||||
* @DECT_TBC_SETUP_FAILED: Bearer setup failed
|
||||
* @DECT_TBC_SETUP_COMPLETE: Bearer setup complete
|
||||
* @DECT_TBC_HANDSHAKE_TIMEOUT: RFPI handshake timeout
|
||||
* @DECT_TBC_REMOTE_RELEASE: Release procedure initiated by remote side
|
||||
*/
|
||||
enum dect_tbc_event {
|
||||
DECT_TBC_SETUP_FAILED,
|
||||
DECT_TBC_SETUP_COMPLETE,
|
||||
DECT_TBC_HANDSHAKE_TIMEOUT,
|
||||
DECT_TBC_REMOTE_RELEASE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_tbc - DECT Traffic Bearer Control
|
||||
*
|
||||
* @list: device TBC list node
|
||||
* @cell: DECT cell
|
||||
* @id: ID of associated MBC
|
||||
* @txb: TX bearer
|
||||
* @rxb: RX bearer
|
||||
* @state: Bearer establishment state
|
||||
* @tx_timer: Transmit activation timer
|
||||
* @wd_timer: Receive watchdog timer
|
||||
* @release_timer: Release timer for unacknowledged release procedure
|
||||
* @normal_tx_timer: Normal transmit timer for C-channel/I_N normal delay transmission
|
||||
* @tx_timer: Minimum delay transmit timer
|
||||
* @c_tx_skb: C_S segment for next TDMA frame
|
||||
* @c_tx_pkt: C_S segment sequence number
|
||||
* @b_tx_skb: B-field data segment for next TDMA frame
|
||||
* @bc: Broadcast Control
|
||||
*/
|
||||
struct dect_tbc {
|
||||
struct list_head list;
|
||||
struct dect_cell *cell;
|
||||
struct dect_mbc_id id;
|
||||
|
||||
struct dect_bearer *txb;
|
||||
struct dect_bearer *rxb;
|
||||
|
||||
enum dect_tbc_state state;
|
||||
struct dect_timer wait_timer;
|
||||
struct dect_timer wd_timer;
|
||||
struct dect_timer release_timer;
|
||||
|
||||
struct dect_timer normal_tx_timer;
|
||||
struct dect_timer tx_timer;
|
||||
|
||||
uint8_t c_tx_pkt;
|
||||
struct sk_buff *c_tx_skb;
|
||||
struct sk_buff *b_tx_skb;
|
||||
|
||||
struct dect_bc bc;
|
||||
};
|
||||
|
||||
#define DECT_TBC_RFPI_TIMEOUT (5 * DECT_FRAMES_PER_SECOND)
|
||||
|
||||
enum dect_scan_status {
|
||||
DECT_SCAN_FAIL,
|
||||
DECT_SCAN_TIMEOUT,
|
||||
DECT_SCAN_COMPLETE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_irc - Idle receiver control
|
||||
*
|
||||
* @cell: DECT cell
|
||||
* @trx: DECT transceiver
|
||||
* @ari: ARI filter
|
||||
* @ari_mask: ARI filter mask
|
||||
* @idi: identities information
|
||||
* @si: system information
|
||||
* @notify: notification callback
|
||||
* @rx_scn: Scan carrier number (RX time base)
|
||||
* @tx_scn: Scan carrier number (TX time base)
|
||||
* @rx_frame_timer: rx_scn update timer
|
||||
* @tx_frame_timer: tx_scn update timer
|
||||
*/
|
||||
struct dect_irc {
|
||||
struct dect_cell *cell;
|
||||
struct dect_transceiver *trx;
|
||||
|
||||
struct dect_llme_req lreq;
|
||||
|
||||
struct dect_ari ari;
|
||||
struct dect_ari ari_mask;
|
||||
|
||||
u16 timeout;
|
||||
u16 rssi;
|
||||
struct dect_idi idi;
|
||||
struct dect_si si;
|
||||
|
||||
void (*notify)(struct dect_cell *,
|
||||
struct dect_transceiver *,
|
||||
enum dect_scan_status);
|
||||
|
||||
u8 rx_scn;
|
||||
u8 tx_scn;
|
||||
struct dect_timer rx_frame_timer;
|
||||
struct dect_timer tx_frame_timer;
|
||||
struct dect_bearer scan_bearer;
|
||||
};
|
||||
|
||||
#define DECT_IRC_SCN_OFF 3
|
||||
|
||||
struct dect_scan_result {
|
||||
struct dect_llme_req lreq;
|
||||
struct dect_idi idi;
|
||||
struct dect_si si;
|
||||
u8 rssi;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_csf_ops - Cell Site Function ops
|
||||
*
|
||||
* @set_mode: set cell to PP/FP mode
|
||||
* @scan: initiate scan for pari/pari_mask
|
||||
* @preload: preload system information
|
||||
* @enable: enable cell
|
||||
* @page_request: deliver paging message
|
||||
* @tbc_initiate: initiate a new connection
|
||||
* @tbc_confirm: confirm an incoming connection
|
||||
* @tbc_release: release a TBC
|
||||
*
|
||||
* The CSF ops define the interface in the direction CCF -> CSF.
|
||||
*/
|
||||
struct dect_cell_handle;
|
||||
struct dect_csf_ops {
|
||||
int (*set_mode)(const struct dect_cell_handle *,
|
||||
enum dect_cluster_modes);
|
||||
int (*scan)(const struct dect_cell_handle *,
|
||||
const struct dect_llme_req *lreq,
|
||||
const struct dect_ari *, const struct dect_ari *);
|
||||
int (*preload)(const struct dect_cell_handle *,
|
||||
const struct dect_ari *, u8,
|
||||
const struct dect_si *);
|
||||
int (*enable)(const struct dect_cell_handle *);
|
||||
|
||||
void (*page_request)(const struct dect_cell_handle *,
|
||||
struct sk_buff *);
|
||||
|
||||
int (*tbc_initiate)(const struct dect_cell_handle *,
|
||||
const struct dect_mbc_id *,
|
||||
const struct dect_channel_desc *);
|
||||
int (*tbc_confirm)(const struct dect_cell_handle *,
|
||||
const struct dect_mbc_id *);
|
||||
void (*tbc_release)(const struct dect_cell_handle *,
|
||||
const struct dect_mbc_id *);
|
||||
void (*tbc_data_request)(const struct dect_cell_handle *,
|
||||
const struct dect_mbc_id *,
|
||||
enum dect_data_channels chan,
|
||||
struct sk_buff *);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_cell_handle - DECT cluster view of a cell
|
||||
*
|
||||
* @list: cluster cell list node
|
||||
* @clh: bound cluster handle
|
||||
* @ops: cell site function ops
|
||||
* @rpn: assigned radio part number
|
||||
* @portref: cell control protocol port reference (remote cells)
|
||||
*/
|
||||
struct dect_cell_handle {
|
||||
struct list_head list;
|
||||
struct dect_cluster_handle *clh;
|
||||
const struct dect_csf_ops *ops;
|
||||
u8 rpn;
|
||||
|
||||
u32 portref;
|
||||
};
|
||||
|
||||
enum dect_cell_states {
|
||||
DECT_CELL_ENABLED = 1 << 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_cell - DECT cell: one radio system
|
||||
*
|
||||
* @list: cell list node
|
||||
* @name: cells' name
|
||||
* @index: unique numeric cell identifier
|
||||
* @flags: operational and status flags
|
||||
* @handle: cell handle
|
||||
* @lock: lock
|
||||
* @mode: operational mode (FP/PP)
|
||||
* @state: bitmask of enum dect_cell_states
|
||||
* @idi: FP System Identity
|
||||
* @fmid: FMID (Fixed MAC IDentity)
|
||||
* @si: FP System Information
|
||||
* @bcs: Broadcast Controllers
|
||||
* @cbc: Connectionless Bearer Controller
|
||||
* @dbcs: Dummy Bearer Controllers
|
||||
* @tbcs: list of Traffic Bearer Controllers
|
||||
* @chanlists: list of channel lists for different channel types
|
||||
* @timer_base: RX/TX timer bases
|
||||
* @trg: DECT transceiver group
|
||||
*/
|
||||
struct dect_cell {
|
||||
struct list_head list;
|
||||
char name[DECTNAMSIZ];
|
||||
u32 index;
|
||||
u32 flags;
|
||||
|
||||
struct dect_cell_handle handle;
|
||||
|
||||
spinlock_t lock;
|
||||
enum dect_cluster_modes mode;
|
||||
u32 state;
|
||||
|
||||
/* identities */
|
||||
struct dect_idi idi;
|
||||
u16 fmid;
|
||||
|
||||
/* system information */
|
||||
struct dect_si si;
|
||||
u32 blind_full_slots;
|
||||
|
||||
/* Broadcast controllers and related data */
|
||||
struct dect_timer page_timer;
|
||||
struct sk_buff_head page_queue;
|
||||
struct sk_buff_head page_fast_queue;
|
||||
|
||||
struct sk_buff *page_sdu;
|
||||
struct sk_buff_head page_tx_queue;
|
||||
|
||||
struct list_head bcs;
|
||||
unsigned int si_idx;
|
||||
|
||||
struct dect_cbc cbc;
|
||||
struct list_head dbcs;
|
||||
struct list_head tbcs;
|
||||
|
||||
/* channel lists */
|
||||
struct list_head chl_pending;
|
||||
struct list_head chanlists;
|
||||
struct dect_channel_list *chl_next;
|
||||
struct dect_channel_list *chl;
|
||||
|
||||
struct dect_timer_base timer_base[DECT_TIMER_BASE_MAX + 1];
|
||||
struct dect_transceiver_group trg;
|
||||
};
|
||||
|
||||
static inline u8 dect_normal_transmit_base(const struct dect_cell *cell)
|
||||
{
|
||||
return cell->mode == DECT_MODE_FP ? 0 : DECT_HALF_FRAME_SIZE;
|
||||
}
|
||||
|
||||
static inline u8 dect_normal_receive_base(const struct dect_cell *cell)
|
||||
{
|
||||
return cell->mode == DECT_MODE_FP ? DECT_HALF_FRAME_SIZE : 0;
|
||||
}
|
||||
|
||||
#define dect_foreach_transmit_slot(slot, end, cell) \
|
||||
for ((slot) = dect_normal_transmit_base(cell), \
|
||||
(end) = (slot) + DECT_HALF_FRAME_SIZE; \
|
||||
(slot) < (end); (slot)++)
|
||||
|
||||
#define dect_foreach_receive_slot(slot, end, cell) \
|
||||
for ((slot) = dect_normal_receive_base(cell), \
|
||||
(end) = (slot) + DECT_HALF_FRAME_SIZE; \
|
||||
(slot) < (end); (slot)++)
|
||||
|
||||
extern struct dect_cell *dect_cell_get_by_index(u32 index);
|
||||
|
||||
extern void dect_cell_init(struct dect_cell *cell);
|
||||
extern int dect_cell_bind(struct dect_cell *cell, u8 index);
|
||||
extern void dect_cell_shutdown(struct dect_cell *cell);
|
||||
|
||||
extern int dect_cell_attach_transceiver(struct dect_cell *cell,
|
||||
struct dect_transceiver *trx);
|
||||
extern void dect_cell_detach_transceiver(struct dect_cell *cell,
|
||||
struct dect_transceiver *trx);
|
||||
|
||||
extern void dect_mac_rcv(struct dect_transceiver *trx,
|
||||
struct dect_transceiver_slot *ts,
|
||||
struct sk_buff *skb);
|
||||
extern void dect_mac_report_rssi(struct dect_transceiver *trx,
|
||||
struct dect_transceiver_slot *ts, u8 rssi);
|
||||
extern void dect_mac_rx_tick(struct dect_transceiver_group *grp, u8 slot);
|
||||
extern void dect_mac_tx_tick(struct dect_transceiver_group *grp, u8 slot);
|
||||
|
||||
extern void dect_mac_irc_rcv(struct dect_transceiver *trx, struct sk_buff *skb);
|
||||
extern void dect_mac_irc_tick(struct dect_transceiver *trx);
|
||||
|
||||
#endif /* _NET_DECT_MAC_CSF_H */
|
|
@ -0,0 +1,633 @@
|
|||
/*
|
||||
* DECT Transceiver Layer
|
||||
*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*/
|
||||
|
||||
#ifndef _NET_DECT_TRANSCEIVER_H
|
||||
#define _NET_DECT_TRANSCEIVER_H
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/dect.h>
|
||||
#include <linux/dect_netlink.h>
|
||||
|
||||
#define DECT_RSSI_RANGE 255
|
||||
#define DECT_RSSI_DBM_LOW -93
|
||||
#define DECT_RSSI_DBM_RANGE 60
|
||||
|
||||
static inline u8 dect_dbm_to_rssi_rel(s8 dbm)
|
||||
{
|
||||
return dbm * DECT_RSSI_RANGE / DECT_RSSI_DBM_RANGE;
|
||||
}
|
||||
|
||||
static inline u8 dect_dbm_to_rssi(s8 dbm)
|
||||
{
|
||||
return dect_dbm_to_rssi_rel(dbm - DECT_RSSI_DBM_LOW);
|
||||
}
|
||||
|
||||
#define DECT_RSSI_AVG_SCALE 3
|
||||
|
||||
static inline u16 dect_average_rssi(u16 cur, u16 sample)
|
||||
{
|
||||
if (cur == 0)
|
||||
cur = sample << DECT_RSSI_AVG_SCALE;
|
||||
else {
|
||||
cur -= cur >> DECT_RSSI_AVG_SCALE;
|
||||
cur += sample;
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
#define DECT_CARRIER_NUM 64
|
||||
|
||||
static inline u8 dect_next_carrier(u64 rfcars, u8 carrier)
|
||||
{
|
||||
u64 tmp;
|
||||
|
||||
if (WARN_ON(rfcars == 0))
|
||||
return 0;
|
||||
tmp = rfcars & ~((1ULL << (carrier + 1)) - 1);
|
||||
if (tmp == 0)
|
||||
tmp = rfcars;
|
||||
return ffs(tmp) - 1;
|
||||
}
|
||||
|
||||
static inline u8 dect_prev_carrier(u64 rfcars, u8 carrier)
|
||||
{
|
||||
u64 tmp;
|
||||
|
||||
if (WARN_ON(rfcars == 0))
|
||||
return 0;
|
||||
tmp = rfcars & ((1ULL << carrier) - 1);
|
||||
if (tmp == 0)
|
||||
tmp = rfcars;
|
||||
return fls(tmp) - 1;
|
||||
}
|
||||
|
||||
static inline u8 dect_carrier_sub(u64 rfcars, u8 carrier, u8 n)
|
||||
{
|
||||
while (n != 0) {
|
||||
carrier = dect_prev_carrier(rfcars, carrier);
|
||||
n--;
|
||||
}
|
||||
return carrier;
|
||||
}
|
||||
|
||||
static inline u8 dect_carrier_distance(u64 rfcars, u8 from, u8 to)
|
||||
{
|
||||
if (from >= to) {
|
||||
/* clear bits between to and from */
|
||||
rfcars &= ~(((1ULL << (from - to)) - 1) << to);
|
||||
} else {
|
||||
/* clear bits not between from and to */
|
||||
rfcars &= ((1ULL << (to - from)) - 1) << from;
|
||||
}
|
||||
return hweight64(rfcars);
|
||||
}
|
||||
|
||||
#define DECT_BAND_NUM 32
|
||||
#define DECT_DEFAULT_BAND 0
|
||||
|
||||
#define DECT_FREQUENCY_F0 1897344 /* kHz */
|
||||
#define DECT_CARRIER_WIDTH 1728 /* kHz */
|
||||
|
||||
/**
|
||||
* struct dect_band - DECT RF-band
|
||||
*
|
||||
* @band: RF-band number
|
||||
* @carriers: number of defined carriers
|
||||
* @frequency: frequency of each carrier in kHz
|
||||
*/
|
||||
struct dect_band {
|
||||
u8 band;
|
||||
u8 carriers;
|
||||
u32 frequency[];
|
||||
};
|
||||
|
||||
#define DECT_FRAME_SIZE 24
|
||||
#define DECT_HALF_FRAME_SIZE (DECT_FRAME_SIZE / 2)
|
||||
#define DECT_FRAMES_PER_SECOND 100
|
||||
|
||||
#define DECT_SCAN_SLOT 0
|
||||
#define DECT_SLOT_MASK 0x00ffffff
|
||||
|
||||
static inline u8 dect_next_slotnum(u8 slot)
|
||||
{
|
||||
if (++slot == DECT_FRAME_SIZE)
|
||||
slot = 0;
|
||||
return slot;
|
||||
}
|
||||
|
||||
static inline u8 dect_slot_add(u8 s1, u8 s2)
|
||||
{
|
||||
return (s1 + s2) % DECT_FRAME_SIZE;
|
||||
}
|
||||
|
||||
static inline u8 dect_slot_distance(u8 s1, u8 s2)
|
||||
{
|
||||
return s2 >= s1 ? s2 - s1 : DECT_FRAME_SIZE + s2 - s1;
|
||||
}
|
||||
|
||||
#define dect_foreach_slot(slot) \
|
||||
for ((slot) = 0; (slot) < DECT_FRAME_SIZE; (slot)++)
|
||||
|
||||
/**
|
||||
* enum dect_slot_types - DECT slot types
|
||||
*
|
||||
* @DECT_HALF_SLOT: Half-slot format (240 bits)
|
||||
* @DECT_FULL_SLOT: Full-slot format (480 bits)
|
||||
* @DECT_DOUBLE_SLOT: Double-slot format (960 bits)
|
||||
*/
|
||||
enum dect_slot_types {
|
||||
DECT_HALF_SLOT,
|
||||
DECT_FULL_SLOT,
|
||||
DECT_DOUBLE_SLOT,
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
enum dect_packet_types {
|
||||
DECT_PACKET_P00,
|
||||
DECT_PACKET_P08,
|
||||
DECT_PACKET_P32,
|
||||
DECT_PACKET_P80,
|
||||
__DECT_PACKET_MAX
|
||||
};
|
||||
#define DECT_PACKET_MAX (__DECT_PACKET_MAX - 1)
|
||||
|
||||
enum dect_packet_sizes {
|
||||
DECT_P00_SIZE = 12,
|
||||
DECT_P08_SIZE = 23,
|
||||
DECT_P32_SIZE = 53,
|
||||
DECT_P80_SIZE = 113,
|
||||
};
|
||||
|
||||
#define DECT_PREAMBLE_SIZE 4
|
||||
|
||||
/**
|
||||
* enum dect_b_formats - DECT B-Field formats
|
||||
*
|
||||
* @DECT_B_NONE: No B-field
|
||||
* @DECT_B_UNPROTECTED: Unprotected B-field format
|
||||
* @DECT_B_PROTECTED: Protected B-field format
|
||||
*
|
||||
* The B-Field format can be used by a transceiver for offloading X-CRC
|
||||
* calculation.
|
||||
*/
|
||||
enum dect_b_formats {
|
||||
DECT_B_NONE,
|
||||
DECT_B_UNPROTECTED,
|
||||
DECT_B_PROTECTED,
|
||||
__DECT_B_MAX
|
||||
};
|
||||
#define DECT_B_MAX (__DECT_B_MAX - 1)
|
||||
|
||||
/**
|
||||
* struct dect_channel_desc - DECT physical channel description
|
||||
*
|
||||
* @pkt: Packet type in use
|
||||
* @b_fmt: B-Field format for checksum offloading
|
||||
* @slot: Slot number
|
||||
* @carrier: RF-carrier number
|
||||
*/
|
||||
struct dect_channel_desc {
|
||||
enum dect_packet_types pkt;
|
||||
enum dect_b_formats b_fmt;
|
||||
u8 slot;
|
||||
u8 carrier;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_transceiver_slot - Transceiver TDMA slot
|
||||
*
|
||||
* @state: current state
|
||||
* @desc: channel description
|
||||
* @bearer: associated bearer
|
||||
* @rssi: averaged RSSI
|
||||
* @rx_bytes: RX byte count
|
||||
* @rx_packets: RX packet count
|
||||
* @tx_bytes: TX byte count
|
||||
* @tx_packets: TX packet count
|
||||
*/
|
||||
struct dect_transceiver_slot {
|
||||
enum dect_slot_states state;
|
||||
struct dect_channel_desc chd;
|
||||
struct dect_bearer *bearer;
|
||||
|
||||
u16 rssi;
|
||||
u32 rx_bytes;
|
||||
u32 rx_packets;
|
||||
u32 tx_bytes;
|
||||
u32 tx_packets;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_transceiver_event - one atomic unit of work for the MAC layer
|
||||
*
|
||||
* @trx: transceiver
|
||||
* @busy: synchronizer
|
||||
* @list: transceiver group events list node
|
||||
* @rx_queue: received packets
|
||||
* @rssi: RSSI measurement in scanning slots
|
||||
* @rssi_mask: RSSI measurement positions
|
||||
* @slotpos: transceiver slot position in TDMA frame
|
||||
*
|
||||
* A transceiver operates asynchronously to the MAC layer, but the MAC layer's
|
||||
* timing needs to be strictly synchronized to the receiver.
|
||||
*
|
||||
* This structure contains the packets from multiple consequitive slots received
|
||||
* by the receiver in one unit (up to ops->eventrate frames). Slotpos specifies
|
||||
* the transceivers current position in the TDMA frame (== the minimum current
|
||||
* time) and is used for timing purposes and slot maintenance operations of the
|
||||
* upcoming slots. A transceiver uses a fixed amount of these structure and
|
||||
* synchronizes with BH processing through the busy marker. When BH processing
|
||||
* is too slow, frames are dropped.
|
||||
*/
|
||||
struct dect_transceiver_event {
|
||||
struct dect_transceiver *trx;
|
||||
atomic_t busy;
|
||||
struct list_head list;
|
||||
struct sk_buff_head rx_queue;
|
||||
u8 rssi[DECT_HALF_FRAME_SIZE / 2];
|
||||
u8 rssi_mask;
|
||||
u8 slotpos;
|
||||
};
|
||||
|
||||
struct dect_skb_trx_cb {
|
||||
struct dect_transceiver *trx;
|
||||
u32 mfn;
|
||||
u8 frame;
|
||||
u8 slot;
|
||||
u8 rssi;
|
||||
};
|
||||
|
||||
static inline struct dect_skb_trx_cb *DECT_TRX_CB(const struct sk_buff *skb)
|
||||
{
|
||||
BUILD_BUG_ON(sizeof(struct dect_skb_trx_cb) > sizeof(skb->cb));
|
||||
return (struct dect_skb_trx_cb *)skb->cb;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct dect_transceiver_ops - DECT transceiver operations
|
||||
*
|
||||
* @disable: shut the transceiver down
|
||||
* @enable: bring the transceiver to operational state
|
||||
* @confirm: confirm a received signal in slave mode
|
||||
* @unlock: release a confirmed signal again
|
||||
* @lock: lock to a signal
|
||||
* @set_mode: set the mode (RX/TX/SCANNING) for a slot
|
||||
* @set_carrier: set the RF-carrier for a slot
|
||||
* @set_band: set the RF-band
|
||||
* @destructor: destructor
|
||||
* @name transceiver driver name
|
||||
* @slotmask: bitmask of available slots
|
||||
* @eventrate: rate at which slot events are generated, must be integral
|
||||
* divisor of the number of slots per TDMA half frame
|
||||
* @latency: latency in slots until updates for a slot take effect
|
||||
*
|
||||
* A transceiver provides frame reception and transmission, signal strength
|
||||
* measurement as well as a reference clock for the MAC layer. It can exist
|
||||
* in two basic states:
|
||||
*
|
||||
* - master: doesn't need initial synchronization to a radio signal
|
||||
* - slave: needs to synchronize timing with a signal
|
||||
*
|
||||
* Only the first transceiver of a FP is a master, PPs are always slaves to
|
||||
* a FPs timing. Secondary and further transceivers of a FP also start as
|
||||
* slaves until they have synchronized to one of the already running
|
||||
* transceivers.
|
||||
*
|
||||
* Locking to a new signal works in multiple phases:
|
||||
*
|
||||
* 1) The ->enable() callback is invoked. The driver is expected to initiate a
|
||||
* scan for a signal, during which it will pass on any received frame to the
|
||||
* transceiver layer. As no framing has been established, all packets should
|
||||
* indicate a slot number of zero.
|
||||
*
|
||||
* 2) While scanning for a signal, the ->set_carrier() callback may be invoked
|
||||
* with a slot number of zero. The driver is expected to adjust the carrier
|
||||
* on which it is scanning for a signal.
|
||||
*
|
||||
* 3) When the MAC layer determines interest in a received signal, the ->confirm()
|
||||
* callback is invoked. The driver is expected to continue to pass frames from
|
||||
* this signal to the MAC layer to establish framing.
|
||||
*
|
||||
* 3a) When the MAC layer is only collecting information for a scan, it may call
|
||||
* the ->unlock callback to release a previously confirmed signal.
|
||||
*
|
||||
* 4) Once the MAC layer has determined framing relative to the slot timing, the
|
||||
* ->lock() callback is invoked. At this point, only a single physical channel
|
||||
* is received. The driver should synchronize the hardware to the framing to
|
||||
* make it interrupt at the appropriate times.
|
||||
*
|
||||
*/
|
||||
struct dect_transceiver;
|
||||
struct dect_transceiver_ops {
|
||||
void (*disable)(const struct dect_transceiver *trx);
|
||||
void (*enable)(const struct dect_transceiver *trx);
|
||||
|
||||
void (*confirm)(const struct dect_transceiver *trx);
|
||||
void (*unlock)(const struct dect_transceiver *trx);
|
||||
void (*lock)(const struct dect_transceiver *trx, u8 slot);
|
||||
|
||||
void (*set_mode)(const struct dect_transceiver *trx,
|
||||
const struct dect_channel_desc *chd,
|
||||
enum dect_slot_states mode);
|
||||
void (*set_carrier)(const struct dect_transceiver *trx,
|
||||
u8 slot, u8 carrier);
|
||||
void (*tx)(const struct dect_transceiver *trx,
|
||||
struct sk_buff *skb);
|
||||
|
||||
u64 (*set_band)(const struct dect_transceiver *trx,
|
||||
const struct dect_band *band);
|
||||
void (*destructor)(struct dect_transceiver *trx);
|
||||
const char *name;
|
||||
|
||||
u32 slotmask;
|
||||
u8 eventrate;
|
||||
u8 latency;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum dect_transceiver_modes - Transceiver synchronization modes
|
||||
*
|
||||
* @DECT_TRANSCEIVER_MASTER: Transceiver determines reference time (FP)
|
||||
* @DECT_TRANSCEIVER_SLAVE: Transceiver is slave to foreign reference timing
|
||||
*/
|
||||
enum dect_transceiver_modes {
|
||||
DECT_TRANSCEIVER_MASTER,
|
||||
DECT_TRANSCEIVER_SLAVE,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum dect_transceiver_states - transceiver synchronization states
|
||||
*
|
||||
* @DECT_TRANSCEIVER_STOPPED: transceiver is inactive
|
||||
* @DECT_TRANSCEIVER_UNLOCKED: transceiver is not synchronized to any RFP
|
||||
* @DECT_TRANSCEIVER_LOCK_PENDING: transceiver is receiving RFP transmissions,
|
||||
* but has not obtained frame synchonization
|
||||
* @DECT_TRANSCEIVER_LOCKED: the transceiver has achieved frame and
|
||||
* multiframe lock to an RFP
|
||||
*
|
||||
* These correspond to the ETS 300 175-3 Annex D PT MAC layer states, but are
|
||||
* per transceiver as we also need to synchronize secondary transceivers.
|
||||
*/
|
||||
enum dect_transceiver_states {
|
||||
DECT_TRANSCEIVER_STOPPED,
|
||||
DECT_TRANSCEIVER_UNLOCKED,
|
||||
DECT_TRANSCEIVER_LOCK_PENDING,
|
||||
DECT_TRANSCEIVER_LOCKED,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_transceiver_stats - transceiver statistics
|
||||
*
|
||||
* @event_busy: events lost due to MAC layer busy
|
||||
* @event_late: events lost due to transceiver late
|
||||
*/
|
||||
struct dect_transceiver_stats {
|
||||
u32 event_busy;
|
||||
u32 event_late;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dect_transceiver - DECT transceiver
|
||||
*
|
||||
* @list: transceiver list node
|
||||
* @ops: transceiver ops
|
||||
* @name: transceiver identity
|
||||
* @stats: transceiver statistics
|
||||
* @mode: synchronization mode
|
||||
* @state: synchronization state
|
||||
* @band: current RF-band
|
||||
* @carriers: bitmask of supported carriers in the current band
|
||||
* @slots: transceiver slot state
|
||||
* @index: cell transceiver index
|
||||
* @segno: transceiver receive sequence number
|
||||
* @cell: cell the transceiver is assigned to
|
||||
* @irc: idle receiver control
|
||||
* @event: dynamic amount of transceiver event structures
|
||||
*
|
||||
* Following the event structures is the private driver data.
|
||||
*/
|
||||
struct dect_transceiver {
|
||||
struct list_head list;
|
||||
const struct dect_transceiver_ops *ops;
|
||||
char name[DECTNAMSIZ];
|
||||
|
||||
struct dect_transceiver_stats stats;
|
||||
enum dect_transceiver_modes mode;
|
||||
enum dect_transceiver_states state;
|
||||
|
||||
const struct dect_band *band;
|
||||
u64 carriers;
|
||||
|
||||
struct dect_transceiver_slot slots[DECT_FRAME_SIZE];
|
||||
u32 blind_full_slots;
|
||||
|
||||
u8 index;
|
||||
u32 seqno;
|
||||
struct dect_cell *cell;
|
||||
struct dect_irc *irc;
|
||||
struct dect_transceiver_event event[];
|
||||
};
|
||||
|
||||
static inline void *dect_transceiver_priv(const struct dect_transceiver *trx)
|
||||
{
|
||||
return (void *)&trx->event[DECT_HALF_FRAME_SIZE / trx->ops->eventrate];
|
||||
}
|
||||
|
||||
static inline bool dect_slot_available(const struct dect_transceiver *trx, u8 slot)
|
||||
{
|
||||
return trx->ops->slotmask & (1 << slot);
|
||||
}
|
||||
|
||||
extern struct dect_transceiver *dect_transceiver_alloc(const struct dect_transceiver_ops *ops,
|
||||
unsigned int priv);
|
||||
extern void dect_transceiver_free(struct dect_transceiver *trx);
|
||||
extern int dect_register_transceiver(struct dect_transceiver *trx);
|
||||
extern void dect_unregister_transceiver(struct dect_transceiver *trx);
|
||||
|
||||
extern void dect_transceiver_enable(struct dect_transceiver *trx);
|
||||
extern void dect_transceiver_disable(struct dect_transceiver *trx);
|
||||
|
||||
extern void dect_transceiver_confirm(struct dect_transceiver *trx);
|
||||
extern void dect_transceiver_unlock(struct dect_transceiver *trx);
|
||||
extern void dect_transceiver_lock(struct dect_transceiver *trx, u8 slot);
|
||||
|
||||
extern int dect_transceiver_set_band(struct dect_transceiver *trx, u8 bandnum);
|
||||
|
||||
static inline void dect_set_channel_mode(struct dect_transceiver *trx,
|
||||
const struct dect_channel_desc *chd,
|
||||
enum dect_slot_states mode)
|
||||
{
|
||||
trx->ops->set_mode(trx, chd, mode);
|
||||
trx->slots[chd->slot].state = mode;
|
||||
trx->slots[chd->slot].chd.pkt = chd->pkt;
|
||||
trx->slots[chd->slot].chd.b_fmt = chd->b_fmt;
|
||||
}
|
||||
|
||||
static inline void dect_set_carrier(struct dect_transceiver *trx,
|
||||
u8 slot, u8 carrier)
|
||||
{
|
||||
trx->slots[slot].chd.carrier = carrier;
|
||||
trx->slots[slot].rssi = 0;
|
||||
trx->ops->set_carrier(trx, slot, carrier);
|
||||
}
|
||||
|
||||
static inline void dect_transceiver_tx(struct dect_transceiver *trx,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
u8 slot = DECT_TRX_CB(skb)->slot;
|
||||
|
||||
trx->ops->tx(trx, skb);
|
||||
trx->slots[slot].tx_bytes += skb->len;
|
||||
trx->slots[slot].tx_packets++;
|
||||
}
|
||||
|
||||
extern struct sk_buff *dect_transceiver_alloc_skb(struct dect_transceiver *trx, u8 slot);
|
||||
|
||||
static inline struct dect_transceiver_event *
|
||||
dect_transceiver_event(struct dect_transceiver *trx, u8 n, u8 slotpos)
|
||||
{
|
||||
struct dect_transceiver_event *event;
|
||||
|
||||
event = &trx->event[n];
|
||||
if (unlikely(!atomic_add_unless(&event->busy, 1, 1))) {
|
||||
trx->stats.event_busy++;
|
||||
return NULL;
|
||||
}
|
||||
event->slotpos = slotpos;
|
||||
return event;
|
||||
}
|
||||
|
||||
static inline void dect_transceiver_record_rssi(struct dect_transceiver_event *event,
|
||||
u8 slot, u8 rssi)
|
||||
{
|
||||
u8 idx;
|
||||
|
||||
idx = slot % event->trx->ops->eventrate;
|
||||
event->rssi[idx] = rssi;
|
||||
event->rssi_mask |= 1 << idx;
|
||||
}
|
||||
|
||||
static inline void dect_release_transceiver_event(struct dect_transceiver_event *event)
|
||||
{
|
||||
event->rssi_mask = 0;
|
||||
smp_mb__before_atomic_dec();
|
||||
atomic_dec(&event->busy);
|
||||
}
|
||||
|
||||
extern struct list_head dect_transceiver_list;
|
||||
|
||||
enum dect_transceiver_events {
|
||||
DECT_TRANSCEIVER_REGISTER,
|
||||
DECT_TRANSCEIVER_UNREGISTER,
|
||||
};
|
||||
|
||||
extern void dect_register_notifier(struct notifier_block *nb);
|
||||
extern void dect_unregister_notifier(struct notifier_block *nb);
|
||||
|
||||
#define DECT_TRX_GROUP_MAX 16
|
||||
|
||||
/**
|
||||
* struct dect_transceiver_group
|
||||
*
|
||||
* @trx: Transceiver array
|
||||
* @trxmask: Mask of present transceivers
|
||||
* @latency: Maximum latency of all transceivers
|
||||
* @blind_full_slots: combined blind full slots state of all transceivers
|
||||
* @tasklet: Event processing tasklet
|
||||
* @lock: Event list lock
|
||||
* @events: List of queued events
|
||||
* @seqno: Transceiver event loss detection
|
||||
* @slot_low: First unhandled slot
|
||||
* @slot_high: First slot after slot window
|
||||
* @slots: merged events for window slot_low - slot_high
|
||||
*/
|
||||
struct dect_transceiver_group {
|
||||
struct dect_transceiver *trx[DECT_TRX_GROUP_MAX];
|
||||
u16 trxmask;
|
||||
u8 latency;
|
||||
u32 blind_full_slots;
|
||||
|
||||
struct tasklet_struct tasklet;
|
||||
spinlock_t lock;
|
||||
struct list_head events;
|
||||
|
||||
u32 seqno;
|
||||
u8 slot_low;
|
||||
u8 slot_high;
|
||||
struct {
|
||||
struct sk_buff_head queue;
|
||||
u16 mask;
|
||||
u8 rssi[DECT_TRX_GROUP_MAX];
|
||||
} slots[DECT_HALF_FRAME_SIZE];
|
||||
};
|
||||
|
||||
extern void dect_transceiver_group_init(struct dect_transceiver_group *trg);
|
||||
extern int dect_transceiver_group_add(struct dect_transceiver_group *trg,
|
||||
struct dect_transceiver *trx);
|
||||
extern void dect_transceiver_group_remove(struct dect_transceiver_group *trg,
|
||||
struct dect_transceiver *trx);
|
||||
|
||||
extern bool dect_transceiver_channel_available(const struct dect_transceiver *trx,
|
||||
const struct dect_channel_desc *chd);
|
||||
extern bool dect_transceiver_reserve(struct dect_transceiver_group *trg,
|
||||
struct dect_transceiver *trx,
|
||||
const struct dect_channel_desc *chd);
|
||||
extern bool dect_transceiver_release(struct dect_transceiver_group *trg,
|
||||
struct dect_transceiver *trx,
|
||||
const struct dect_channel_desc *chd);
|
||||
|
||||
extern void dect_transceiver_queue_event(struct dect_transceiver *trx,
|
||||
struct dect_transceiver_event *ev);
|
||||
|
||||
#define dect_first_transceiver(trg) \
|
||||
({ \
|
||||
struct dect_transceiver_group *_trg = (void *)(trg); \
|
||||
u32 mask = _trg->trxmask; \
|
||||
mask ? (_trg)->trx[ffs(mask) - 1] : NULL; })
|
||||
|
||||
#define dect_next_transceiver(trx, trg) \
|
||||
({ \
|
||||
struct dect_transceiver_group *_trg = (void *)(trg); \
|
||||
u32 mask = _trg->trxmask; \
|
||||
mask &= ~((1 << ((trx)->index + 1)) - 1); \
|
||||
mask ? (_trg)->trx[ffs(mask) - 1] : NULL; })
|
||||
|
||||
#define dect_foreach_transceiver(trx, trg) \
|
||||
for ((trx) = dect_first_transceiver(trg); \
|
||||
(trx) != NULL; \
|
||||
(trx) = dect_next_transceiver(trx, trg))
|
||||
|
||||
#define dect_last_transceiver(trg) \
|
||||
({ \
|
||||
struct dect_transceiver_group *_trg = (void *)(trg); \
|
||||
u32 mask = _trg->trxmask; \
|
||||
mask ? (_trg)->trx[fls(mask) - 1] : NULL; })
|
||||
|
||||
#define dect_prev_transceiver(trx, trg) \
|
||||
({ \
|
||||
struct dect_transceiver_group *_trg = (void *)(trg); \
|
||||
u32 mask = _trg->trxmask; \
|
||||
mask &= (1 << (trx)->index) - 1; \
|
||||
mask ? (_trg)->trx[fls(mask) - 1] : NULL; })
|
||||
|
||||
#define dect_foreach_transceiver_reverse(trx, trg) \
|
||||
for ((trx) = dect_last_transceiver(trg); \
|
||||
(trx) != NULL; \
|
||||
(trx) = dect_prev_transceiver(trx, trg))
|
||||
|
||||
extern int dect_transceiver_module_init(void);
|
||||
extern void dect_transceiver_module_exit(void);
|
||||
|
||||
#endif /* _NET_DECT_TRANSCEIVER_H */
|
|
@ -239,6 +239,7 @@ source "net/ax25/Kconfig"
|
|||
source "net/can/Kconfig"
|
||||
source "net/irda/Kconfig"
|
||||
source "net/bluetooth/Kconfig"
|
||||
source "net/dect/Kconfig"
|
||||
source "net/rxrpc/Kconfig"
|
||||
|
||||
config FIB_RULES
|
||||
|
|
|
@ -38,6 +38,7 @@ obj-$(CONFIG_AX25) += ax25/
|
|||
obj-$(CONFIG_CAN) += can/
|
||||
obj-$(CONFIG_IRDA) += irda/
|
||||
obj-$(CONFIG_BT) += bluetooth/
|
||||
obj-$(CONFIG_DECT) += dect/
|
||||
obj-$(CONFIG_SUNRPC) += sunrpc/
|
||||
obj-$(CONFIG_AF_RXRPC) += rxrpc/
|
||||
obj-$(CONFIG_ATM) += atm/
|
||||
|
|
|
@ -155,7 +155,7 @@ static const char *af_family_key_strings[AF_MAX+1] = {
|
|||
"sk_lock-27" , "sk_lock-28" , "sk_lock-AF_CAN" ,
|
||||
"sk_lock-AF_TIPC" , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV" ,
|
||||
"sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN" , "sk_lock-AF_PHONET" ,
|
||||
"sk_lock-AF_MAX"
|
||||
"sk-lock-AF_DECT" , "sk_lock-AF_MAX"
|
||||
};
|
||||
static const char *af_family_slock_key_strings[AF_MAX+1] = {
|
||||
"slock-AF_UNSPEC", "slock-AF_UNIX" , "slock-AF_INET" ,
|
||||
|
@ -170,7 +170,7 @@ static const char *af_family_slock_key_strings[AF_MAX+1] = {
|
|||
"slock-27" , "slock-28" , "slock-AF_CAN" ,
|
||||
"slock-AF_TIPC" , "slock-AF_BLUETOOTH", "slock-AF_IUCV" ,
|
||||
"slock-AF_RXRPC" , "slock-AF_ISDN" , "slock-AF_PHONET" ,
|
||||
"slock-AF_MAX"
|
||||
"slock-AF_DECT" , "slock-AF_MAX"
|
||||
};
|
||||
static const char *af_family_clock_key_strings[AF_MAX+1] = {
|
||||
"clock-AF_UNSPEC", "clock-AF_UNIX" , "clock-AF_INET" ,
|
||||
|
@ -185,7 +185,7 @@ static const char *af_family_clock_key_strings[AF_MAX+1] = {
|
|||
"clock-27" , "clock-28" , "clock-AF_CAN" ,
|
||||
"clock-AF_TIPC" , "clock-AF_BLUETOOTH", "clock-AF_IUCV" ,
|
||||
"clock-AF_RXRPC" , "clock-AF_ISDN" , "clock-AF_PHONET" ,
|
||||
"clock-AF_MAX"
|
||||
"clock-AF_DECT" , "clock-AF_MAX"
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
menuconfig DECT
|
||||
tristate "DECT protocol support"
|
||||
help
|
||||
This option enables support for the DECT protocol.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
if DECT
|
||||
|
||||
config DECT_CSF
|
||||
tristate "DECT Cell Site Functions (CSF) support"
|
||||
help
|
||||
This option enables support for DECT Cell Site Functions. A DECT
|
||||
cell is a radio endpoint containing one or more transceivers.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config DECT_RAW
|
||||
tristate "DECT raw sockets"
|
||||
depends on DECT_CSF
|
||||
help
|
||||
This option enables support for PF_DECT raw sockets. DECT raw
|
||||
sockets are used to receive raw frames from DECT devices.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config DECT_CCF
|
||||
tristate "DECT Cluster Control Functions (CCF) support"
|
||||
help
|
||||
This option enables support for the DECT Cluster Control Functions.
|
||||
|
||||
A DECT cluster is a Portable radio Termination (PT), containing a
|
||||
single cell, or a Fixed radio Termination (FT), containing up to
|
||||
8 cells.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config DECT_LU1_SAP
|
||||
tristate "DECT DLC LU1 SAP sockets"
|
||||
select DECT_CCF
|
||||
help
|
||||
This option enables support for PF_DECT DLC LU1 SAP sockets. DECT
|
||||
DLC LU1 SAP sockets are used for the TRUP (TRansparent UnProtected)
|
||||
service, most commonly used for audio.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config DECT_CCP
|
||||
tristate "DECT Cell Control Protocol support"
|
||||
depends on DECT_CSF || DECT_CCF
|
||||
select TIPC
|
||||
help
|
||||
This option enables support for the DECT Cell Control Protocol.
|
||||
This can be used to remotely control one or multiple DECT cells
|
||||
by the DECT cluster control functions.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
endif
|
|
@ -0,0 +1,17 @@
|
|||
dect-y += core.o identities.o dect_netlink.o af_dect.o
|
||||
|
||||
dect_csf-y += mac_csf.o transceiver.o
|
||||
|
||||
dect_ccf-y += mac_ccf.o
|
||||
dect_ccf-y += dlc.o
|
||||
dect_ccf-y += dlc_cplane.o dlc_b_sap.o dlc_s_sap.o
|
||||
dect_ccf-y += dlc_uplane.o dlc_lu1_sap.o
|
||||
|
||||
dect-y += $(dect_ccf-y) $(dect_csf-y) ccp.o
|
||||
|
||||
obj-$(CONFIG_DECT) += dect.o
|
||||
#obj-$(CONFIG_DECT_CSF) += dect_csf.o
|
||||
obj-$(CONFIG_DECT_RAW) += raw.o
|
||||
#obj-$(CONFIG_DECT_CCF) += dect_ccf.o
|
||||
obj-$(CONFIG_DECT_B_SAP) += dlc_b_sap.o
|
||||
#obj-$(CONFIG_DECT_LU1_SAP) += dlc_lu1_sap.o
|
|
@ -0,0 +1,391 @@
|
|||
/*
|
||||
* DECT sockets
|
||||
*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/dect.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/dect/dect.h>
|
||||
|
||||
static struct dect_proto *dect_protos[DECT_PROTO_NUM];
|
||||
static DEFINE_SPINLOCK(dect_proto_lock);
|
||||
|
||||
void (*dect_raw_rcv_hook)(struct sk_buff *skb);
|
||||
EXPORT_SYMBOL_GPL(dect_raw_rcv_hook);
|
||||
|
||||
int dect_proto_register(struct dect_proto *proto)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = proto_register(&proto->proto, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
spin_lock(&dect_proto_lock);
|
||||
dect_protos[proto->protocol] = proto;
|
||||
spin_unlock(&dect_proto_lock);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dect_proto_register);
|
||||
|
||||
void dect_proto_unregister(struct dect_proto *proto)
|
||||
{
|
||||
spin_lock(&dect_proto_lock);
|
||||
dect_protos[proto->protocol] = NULL;
|
||||
spin_unlock(&dect_proto_lock);
|
||||
proto_unregister(&proto->proto);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dect_proto_unregister);
|
||||
|
||||
static void dect_destruct(struct sock *sk)
|
||||
{
|
||||
__skb_queue_purge(&sk->sk_receive_queue);
|
||||
__skb_queue_purge(&sk->sk_write_queue);
|
||||
}
|
||||
|
||||
static int dect_release(struct socket *sock)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
long timeout;
|
||||
|
||||
if (sk == NULL)
|
||||
return 0;
|
||||
|
||||
timeout = 0;
|
||||
if (sock_flag(sk, SOCK_LINGER) && !(current->flags & PF_EXITING))
|
||||
timeout = sk->sk_lingertime;
|
||||
sock->sk = NULL;
|
||||
sk->sk_prot->close(sk, timeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dect_bind(struct socket *sock, struct sockaddr *uaddr, int len)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
int err;
|
||||
|
||||
err = 0;
|
||||
if (sk->sk_prot->bind != NULL)
|
||||
err = sk->sk_prot->bind(sk, uaddr, len);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dect_listen(struct socket *sock, int backlog)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
int err;
|
||||
|
||||
lock_sock(sk);
|
||||
err = -EINVAL;
|
||||
if (sock->state != SS_UNCONNECTED ||
|
||||
(sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET))
|
||||
goto out;
|
||||
|
||||
if (sk->sk_state != DECT_SK_RELEASED && sk->sk_state != DECT_SK_LISTEN)
|
||||
goto out;
|
||||
|
||||
if (sk->sk_state != DECT_SK_LISTEN)
|
||||
sk->sk_prot->hash(sk);
|
||||
sk->sk_max_ack_backlog = backlog;
|
||||
err = 0;
|
||||
out:
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dect_accept(struct socket *sock, struct socket *newsock, int flags)
|
||||
{
|
||||
struct sock *sk = sock->sk, *newsk;
|
||||
int err;
|
||||
|
||||
newsk = sk->sk_prot->accept(sk, flags, &err);
|
||||
if (newsk == NULL)
|
||||
return err;
|
||||
|
||||
lock_sock(newsk);
|
||||
sock_graft(newsk, newsock);
|
||||
newsock->state = SS_CONNECTED;
|
||||
release_sock(newsk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int dect_poll(struct file *file, struct socket *sock,
|
||||
struct poll_table_struct *wait)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
unsigned int mask;
|
||||
|
||||
poll_wait(file, sk->sk_sleep, wait);
|
||||
mask = 0;
|
||||
|
||||
if (sk->sk_state == DECT_SK_LISTEN) {
|
||||
if (!hlist_empty(&dect_csk(sk)->accept_queue))
|
||||
return POLLIN | POLLRDNORM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* exceptional events? */
|
||||
if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
|
||||
mask |= POLLERR;
|
||||
if (sk->sk_shutdown & RCV_SHUTDOWN)
|
||||
mask |= POLLRDHUP;
|
||||
if (sk->sk_shutdown == SHUTDOWN_MASK)
|
||||
mask |= POLLHUP;
|
||||
|
||||
/* readable? */
|
||||
if (!skb_queue_empty(&sk->sk_receive_queue) ||
|
||||
(sk->sk_shutdown & RCV_SHUTDOWN))
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
|
||||
/* Connection-based need to check for termination and startup */
|
||||
if (sk->sk_state == DECT_SK_RELEASED)
|
||||
mask |= POLLHUP;
|
||||
/* connection hasn't started yet? */
|
||||
if (sk->sk_state == DECT_SK_ESTABLISH_PENDING)
|
||||
return mask;
|
||||
|
||||
/* writable? */
|
||||
if (sock_writeable(sk))
|
||||
mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
|
||||
else
|
||||
set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static int dect_connect(struct socket *sock, struct sockaddr *uaddr, int len,
|
||||
int flags)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
long timeo;
|
||||
int err;
|
||||
|
||||
lock_sock(sk);
|
||||
switch (sock->state) {
|
||||
case SS_CONNECTED:
|
||||
err = -EISCONN;
|
||||
goto out;
|
||||
case SS_CONNECTING:
|
||||
err = -EALREADY;
|
||||
goto out;
|
||||
case SS_UNCONNECTED:
|
||||
err = -EISCONN;
|
||||
if (sk->sk_state != DECT_SK_RELEASED)
|
||||
goto out;
|
||||
err = sk->sk_prot->connect(sk, uaddr, len);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
sock->state = SS_CONNECTING;
|
||||
err = -EINPROGRESS;
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sk->sk_state == DECT_SK_ESTABLISH_PENDING) {
|
||||
timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
|
||||
err = sk_stream_wait_connect(sk, &timeo);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = sock_intr_errno(timeo);
|
||||
if (signal_pending(current))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sk->sk_state == DECT_SK_RELEASED)
|
||||
goto sock_error;
|
||||
|
||||
sock->state = SS_CONNECTED;
|
||||
err = 0;
|
||||
out:
|
||||
release_sock(sk);
|
||||
return err;
|
||||
sock_error:
|
||||
// FIXME
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int dect_getname(struct socket *sock, struct sockaddr *uaddr, int *len,
|
||||
int peer)
|
||||
{
|
||||
const struct dect_proto *p;
|
||||
|
||||
/* AF_DECT uses different address formats for the different SAPs */
|
||||
p = container_of(sock->sk->sk_prot, struct dect_proto, proto);
|
||||
if (p->getname != NULL)
|
||||
return p->getname(sock->sk, uaddr, len, peer);
|
||||
*len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dect_sendmsg(struct kiocb *iocb, struct socket *sock,
|
||||
struct msghdr *msg, size_t size)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
|
||||
return sk->sk_prot->sendmsg(iocb, sk, msg, size);
|
||||
}
|
||||
|
||||
static int dect_create(struct net *net, struct socket *sock, int protocol)
|
||||
{
|
||||
struct dect_proto *p;
|
||||
struct sock *sk;
|
||||
int err = 0;
|
||||
|
||||
if (protocol < 0 || protocol >= DECT_PROTO_NUM)
|
||||
return -EPROTONOSUPPORT;
|
||||
#ifdef CONFIG_MODULES
|
||||
if (dect_protos[protocol] == NULL) {
|
||||
err = request_module("net-pf-%d-proto-%d", PF_DECT, protocol);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
spin_lock(&dect_proto_lock);
|
||||
p = dect_protos[protocol];
|
||||
if (p != NULL && !try_module_get(p->proto.owner))
|
||||
p = NULL;
|
||||
spin_unlock(&dect_proto_lock);
|
||||
|
||||
if (p == NULL)
|
||||
return -EPROTONOSUPPORT;
|
||||
|
||||
if (p->type != sock->type) {
|
||||
err = -EPROTONOSUPPORT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (cap_valid(p->capability) && !capable(p->capability)) {
|
||||
err = -EACCES;
|
||||
goto err;
|
||||
}
|
||||
|
||||
sock->state = SS_UNCONNECTED;
|
||||
sock->ops = p->ops;
|
||||
|
||||
sk = sk_alloc(net, PF_DECT, GFP_KERNEL, &p->proto);
|
||||
if (sk == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
sock_init_data(sock, sk);
|
||||
sk->sk_protocol = protocol;
|
||||
sk->sk_destruct = dect_destruct;
|
||||
|
||||
if (sk->sk_prot->init != NULL) {
|
||||
err = sk->sk_prot->init(sk);
|
||||
if (err < 0) {
|
||||
sock_orphan(sk);
|
||||
sock_put(sk);
|
||||
}
|
||||
}
|
||||
err:
|
||||
module_put(p->proto.owner);
|
||||
return err;
|
||||
}
|
||||
|
||||
const struct proto_ops dect_stream_ops = {
|
||||
.family = PF_DECT,
|
||||
.owner = THIS_MODULE,
|
||||
.release = dect_release,
|
||||
.bind = dect_bind,
|
||||
.connect = dect_connect,
|
||||
.socketpair = sock_no_socketpair,
|
||||
.getname = dect_getname,
|
||||
.poll = dect_poll,
|
||||
.ioctl = sock_no_ioctl,
|
||||
.listen = dect_listen,
|
||||
.accept = dect_accept,
|
||||
.shutdown = sock_no_shutdown,
|
||||
.setsockopt = sock_no_setsockopt,
|
||||
.getsockopt = sock_no_getsockopt,
|
||||
.sendmsg = dect_sendmsg,
|
||||
.recvmsg = sock_common_recvmsg,
|
||||
.mmap = sock_no_mmap,
|
||||
.sendpage = sock_no_sendpage,
|
||||
};
|
||||
|
||||
const struct proto_ops dect_dgram_ops = {
|
||||
.family = PF_DECT,
|
||||
.owner = THIS_MODULE,
|
||||
.release = dect_release,
|
||||
.bind = dect_bind,
|
||||
.connect = sock_no_connect,
|
||||
.socketpair = sock_no_socketpair,
|
||||
.getname = dect_getname,
|
||||
.poll = datagram_poll,
|
||||
.ioctl = sock_no_ioctl,
|
||||
.listen = sock_no_listen,
|
||||
.accept = sock_no_accept,
|
||||
.shutdown = sock_no_shutdown,
|
||||
.setsockopt = sock_no_setsockopt,
|
||||
.getsockopt = sock_no_getsockopt,
|
||||
.sendmsg = dect_sendmsg,
|
||||
.recvmsg = sock_common_recvmsg,
|
||||
.mmap = sock_no_mmap,
|
||||
.sendpage = sock_no_sendpage,
|
||||
};
|
||||
|
||||
static struct net_proto_family dect_family_ops = {
|
||||
.family = PF_DECT,
|
||||
.create = dect_create,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
int __init dect_af_module_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = sock_register(&dect_family_ops);
|
||||
if (err < 0)
|
||||
goto err1;
|
||||
|
||||
err = dect_bsap_module_init();
|
||||
if (err < 0)
|
||||
goto err2;
|
||||
|
||||
err = dect_ssap_module_init();
|
||||
if (err < 0)
|
||||
goto err3;
|
||||
|
||||
err = dect_lu1_sap_module_init();
|
||||
if (err < 0)
|
||||
goto err4;
|
||||
|
||||
return 0;
|
||||
|
||||
err4:
|
||||
dect_ssap_module_exit();
|
||||
err3:
|
||||
dect_bsap_module_exit();
|
||||
err2:
|
||||
sock_unregister(PF_DECT);
|
||||
err1:
|
||||
return err;
|
||||
}
|
||||
|
||||
void dect_af_module_exit(void)
|
||||
{
|
||||
dect_lu1_sap_module_exit();
|
||||
dect_bsap_module_exit();
|
||||
dect_ssap_module_exit();
|
||||
sock_unregister(PF_DECT);
|
||||
}
|
||||
|
||||
MODULE_ALIAS_NETPROTO(PF_DECT);
|
|
@ -0,0 +1,682 @@
|
|||
/*
|
||||
* DECT Cell Control Protocol
|
||||
*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define DEBUG
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/dect.h>
|
||||
#include <net/dect/dect.h>
|
||||
#include <net/dect/mac_csf.h>
|
||||
#include <net/dect/mac_ccf.h>
|
||||
#include <net/dect/ccp.h>
|
||||
#include <net/tipc/tipc.h>
|
||||
|
||||
static struct sk_buff *dect_ccp_msg_alloc(size_t size)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
size += sizeof(struct dect_ccp_msg_hdr) + 2 * LL_MAX_HEADER;
|
||||
skb = alloc_skb(size, GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
return NULL;
|
||||
skb_reserve(skb, size);
|
||||
return skb;
|
||||
}
|
||||
|
||||
static void dect_ccp_build_msg(struct sk_buff *skb,
|
||||
enum dect_ccp_primitives prim)
|
||||
{
|
||||
struct dect_ccp_msg_hdr *h;
|
||||
|
||||
h = (struct dect_ccp_msg_hdr *)skb_push(skb, sizeof(*h));
|
||||
h->primitive = prim;
|
||||
}
|
||||
|
||||
static int dect_ccp_send_to_cell(const struct dect_cell_handle *ch,
|
||||
struct sk_buff *skb,
|
||||
enum dect_ccp_primitives prim)
|
||||
{
|
||||
int err;
|
||||
|
||||
dect_ccp_build_msg(skb, prim);
|
||||
err = tipc_send_buf(ch->portref, skb, skb->len);
|
||||
if (err < 0 && net_ratelimit())
|
||||
printk("Failed to send DECT CCP message\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dect_ccp_send_to_cluster(const struct dect_cluster_handle *clh,
|
||||
struct sk_buff *skb,
|
||||
enum dect_ccp_primitives prim)
|
||||
{
|
||||
int err;
|
||||
|
||||
dect_ccp_build_msg(skb, prim);
|
||||
err = tipc_send_buf(clh->portref, skb, skb->len);
|
||||
if (err < 0 && net_ratelimit())
|
||||
printk("Failed to send DECT CCP message\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
static void dect_ccp_build_mbc_msg(struct sk_buff *skb, const struct dect_mbc_id *id)
|
||||
{
|
||||
struct dect_ccp_mbc_msg *msg;
|
||||
|
||||
msg = (struct dect_ccp_mbc_msg *)__skb_push(skb, sizeof(*msg));
|
||||
msg->mcei = cpu_to_be32(id->mcei);
|
||||
msg->pmid = cpu_to_be32(dect_build_pmid(&id->pmid));
|
||||
msg->ari = cpu_to_be64(dect_build_ari(&id->ari));
|
||||
msg->ecn = id->ecn;
|
||||
}
|
||||
|
||||
static bool dect_ccp_parse_mbc_msg(struct dect_mbc_id *id, struct sk_buff *skb)
|
||||
{
|
||||
struct dect_ccp_mbc_msg *msg;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(*msg)))
|
||||
return false;
|
||||
msg = (struct dect_ccp_mbc_msg *)skb->data;
|
||||
__skb_pull(skb, sizeof(*msg));
|
||||
|
||||
id->mcei = be32_to_cpu(msg->mcei);
|
||||
dect_parse_pmid(&id->pmid, be32_to_cpu(msg->pmid));
|
||||
if (!dect_parse_ari(&id->ari, be64_to_cpu(msg->ari)))
|
||||
return false;
|
||||
id->ecn = msg->ecn;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void dect_ccp_build_sysinfo(struct sk_buff *skb,
|
||||
const struct dect_ari *pari, u8 rpn,
|
||||
const struct dect_si *si)
|
||||
{
|
||||
struct dect_ccp_sysinfo_msg *msg;
|
||||
unsigned int i;
|
||||
|
||||
msg = (struct dect_ccp_sysinfo_msg *)__skb_push(skb, sizeof(*msg));
|
||||
msg->pari = cpu_to_be64(dect_build_ari(pari));
|
||||
for (i = 0; i < si->num_saris; i++)
|
||||
msg->sari[i] = cpu_to_be64(dect_build_ari(&si->sari[i].ari));
|
||||
msg->num_saris = i;
|
||||
msg->fpc = cpu_to_be64(si->fpc.fpc);
|
||||
msg->hlc = cpu_to_be64(si->fpc.hlc);
|
||||
msg->mfn = cpu_to_be32(si->mfn.num);
|
||||
msg->rpn = rpn;
|
||||
}
|
||||
|
||||
static bool dect_ccp_parse_sysinfo(struct dect_ari *pari, u8 *rpn,
|
||||
struct dect_si *si, struct sk_buff *skb)
|
||||
{
|
||||
struct dect_ccp_sysinfo_msg *msg;
|
||||
unsigned int i;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(*msg)))
|
||||
return false;
|
||||
msg = (struct dect_ccp_sysinfo_msg *)skb->data;
|
||||
__skb_pull(skb, sizeof(*msg));
|
||||
|
||||
if (!dect_parse_ari(pari, be64_to_cpu(msg->pari)))
|
||||
return false;
|
||||
*rpn = msg->rpn;
|
||||
|
||||
if (msg->num_saris > ARRAY_SIZE(si->sari))
|
||||
return false;
|
||||
for (i = 0; i < msg->num_saris; i++) {
|
||||
if (!dect_parse_ari(&si->sari[i].ari,
|
||||
be64_to_cpu(msg->sari[i])))
|
||||
return false;
|
||||
}
|
||||
si->fpc.fpc = be64_to_cpu(msg->fpc);
|
||||
si->fpc.hlc = be64_to_cpu(msg->hlc);
|
||||
si->mfn.num = be32_to_cpu(msg->mfn);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int dect_ccp_send_set_mode(const struct dect_cell_handle *ch,
|
||||
enum dect_cluster_modes mode)
|
||||
{
|
||||
struct dect_ccp_mode_msg *msg;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = dect_ccp_msg_alloc(sizeof(*msg));
|
||||
if (skb == NULL)
|
||||
return -ENOMEM;
|
||||
msg = (struct dect_ccp_mode_msg *)__skb_push(skb, sizeof(*msg));
|
||||
msg->mode = mode;
|
||||
|
||||
return dect_ccp_send_to_cell(ch, skb, DECT_CCP_SET_MODE);
|
||||
}
|
||||
|
||||
static void dect_ccp_parse_set_mode(const struct dect_cell_handle *ch,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct dect_ccp_mode_msg *msg;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(*msg)))
|
||||
return;
|
||||
msg = (struct dect_ccp_mode_msg *)skb->data;
|
||||
|
||||
ch->ops->set_mode(ch, msg->mode);
|
||||
}
|
||||
|
||||
static int dect_ccp_send_scan(const struct dect_cell_handle *ch,
|
||||
const struct dect_llme_req *lreq,
|
||||
const struct dect_ari *ari,
|
||||
const struct dect_ari *ari_mask)
|
||||
{
|
||||
struct dect_ccp_scan_msg *msg;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = dect_ccp_msg_alloc(sizeof(*msg));
|
||||
if (skb == NULL)
|
||||
return -ENOMEM;
|
||||
msg = (struct dect_ccp_scan_msg *)__skb_push(skb, sizeof(*msg));
|
||||
msg->ari = cpu_to_be64(dect_build_ari(ari));
|
||||
msg->ari_mask = cpu_to_be64(dect_build_ari(ari_mask));
|
||||
|
||||
return dect_ccp_send_to_cell(ch, skb, DECT_CCP_SCAN);
|
||||
}
|
||||
|
||||
static void dect_ccp_parse_scan(const struct dect_cell_handle *ch,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct dect_ccp_scan_msg *msg;
|
||||
struct dect_ari ari, ari_mask;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(*msg)))
|
||||
return;
|
||||
msg = (struct dect_ccp_scan_msg *)skb->data;
|
||||
|
||||
if (!dect_parse_ari(&ari, be64_to_cpu(msg->ari)))
|
||||
return;
|
||||
if (!dect_parse_ari(&ari_mask, be64_to_cpu(msg->ari_mask)))
|
||||
return;
|
||||
ch->ops->scan(ch, NULL, &ari, &ari_mask);
|
||||
}
|
||||
|
||||
static int dect_ccp_send_preload(const struct dect_cell_handle *ch,
|
||||
const struct dect_ari *pari, u8 rpn,
|
||||
const struct dect_si *si)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = dect_ccp_msg_alloc(sizeof(struct dect_ccp_sysinfo_msg));
|
||||
if (skb == NULL)
|
||||
return -ENOMEM;
|
||||
dect_ccp_build_sysinfo(skb, pari, rpn, si);
|
||||
|
||||
return dect_ccp_send_to_cell(ch, skb, DECT_CCP_PRELOAD);
|
||||
}
|
||||
|
||||
static void dect_ccp_parse_preload(const struct dect_cell_handle *ch,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct dect_ari pari;
|
||||
struct dect_si si;
|
||||
u8 rpn;
|
||||
|
||||
if (!dect_ccp_parse_sysinfo(&pari, &rpn, &si, skb))
|
||||
return;
|
||||
ch->ops->preload(ch, &pari, rpn, &si);
|
||||
}
|
||||
|
||||
static int dect_ccp_send_enable(const struct dect_cell_handle *ch)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = dect_ccp_msg_alloc(0);
|
||||
if (skb == NULL)
|
||||
return -ENOMEM;
|
||||
return dect_ccp_send_to_cell(ch, skb, DECT_CCP_ENABLE);
|
||||
}
|
||||
|
||||
static void dect_ccp_parse_enable(const struct dect_cell_handle *ch,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
ch->ops->enable(ch);
|
||||
}
|
||||
|
||||
static int dect_ccp_send_tbc_initiate(const struct dect_cell_handle *ch,
|
||||
const struct dect_mbc_id *id,
|
||||
const struct dect_channel_desc *cd)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = dect_ccp_msg_alloc(sizeof(struct dect_ccp_mbc_msg));
|
||||
if (skb == NULL)
|
||||
return -ENOMEM;
|
||||
dect_ccp_build_mbc_msg(skb, id);
|
||||
return dect_ccp_send_to_cell(ch, skb, DECT_CCP_TBC_INITIATE);
|
||||
}
|
||||
|
||||
static void dect_ccp_parse_tbc_initiate(const struct dect_cell_handle *ch,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct dect_mbc_id id;
|
||||
|
||||
if (!dect_ccp_parse_mbc_msg(&id, skb))
|
||||
return;
|
||||
ch->ops->tbc_initiate(ch, &id, NULL);
|
||||
}
|
||||
|
||||
static void dect_ccp_parse_tbc_confirm(const struct dect_cell_handle *ch,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct dect_mbc_id id;
|
||||
|
||||
if (!dect_ccp_parse_mbc_msg(&id, skb))
|
||||
return;
|
||||
ch->ops->tbc_confirm(ch, &id);
|
||||
}
|
||||
|
||||
static void dect_ccp_parse_tbc_release(const struct dect_cell_handle *ch,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct dect_mbc_id id;
|
||||
|
||||
if (!dect_ccp_parse_mbc_msg(&id, skb))
|
||||
return;
|
||||
ch->ops->tbc_release(ch, &id);
|
||||
}
|
||||
|
||||
static int dect_ccp_send_tbc_confirm(const struct dect_cell_handle *ch,
|
||||
const struct dect_mbc_id *id)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = dect_ccp_msg_alloc(sizeof(struct dect_ccp_mbc_msg));
|
||||
if (skb == NULL)
|
||||
return -ENOMEM;
|
||||
dect_ccp_build_mbc_msg(skb, id);
|
||||
return dect_ccp_send_to_cell(ch, skb, DECT_CCP_TBC_CONFIRM);
|
||||
}
|
||||
|
||||
static void dect_ccp_send_tbc_release(const struct dect_cell_handle *ch,
|
||||
const struct dect_mbc_id *id)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = dect_ccp_msg_alloc(sizeof(struct dect_ccp_mbc_msg));
|
||||
if (skb == NULL)
|
||||
return;
|
||||
dect_ccp_build_mbc_msg(skb, id);
|
||||
dect_ccp_send_to_cell(ch, skb, DECT_CCP_TBC_RELEASE);
|
||||
}
|
||||
|
||||
static int dect_ccp_send_mbc_conn_indicate(const struct dect_cluster_handle *clh,
|
||||
const struct dect_cell_handle *ch,
|
||||
const struct dect_mbc_id *id)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = dect_ccp_msg_alloc(sizeof(struct dect_ccp_mbc_msg));
|
||||
if (skb == NULL)
|
||||
return -ENOMEM;
|
||||
dect_ccp_build_mbc_msg(skb, id);
|
||||
|
||||
return dect_ccp_send_to_cluster(clh, skb, DECT_CCP_MBC_CONN_INDICATE);
|
||||
}
|
||||
|
||||
static void dect_ccp_parse_mbc_conn_indicate(const struct dect_cell_handle *ch,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
const struct dect_cluster_handle *clh = ch->clh;
|
||||
struct dect_mbc_id id;
|
||||
|
||||
if (!dect_ccp_parse_mbc_msg(&id, skb))
|
||||
return;
|
||||
clh->ops->mbc_conn_indicate(clh, ch, &id);
|
||||
}
|
||||
|
||||
static int dect_ccp_send_mbc_conn_notify(const struct dect_cluster_handle *clh,
|
||||
const struct dect_mbc_id *id,
|
||||
enum dect_tbc_state state)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = dect_ccp_msg_alloc(sizeof(struct dect_ccp_mbc_msg));
|
||||
if (skb == NULL)
|
||||
return -ENOMEM;
|
||||
dect_ccp_build_mbc_msg(skb, id);
|
||||
|
||||
return dect_ccp_send_to_cluster(clh, skb, DECT_CCP_MBC_CONN_NOTIFY);
|
||||
}
|
||||
|
||||
static void dect_ccp_parse_mbc_conn_notify(const struct dect_cell_handle *ch,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
const struct dect_cluster_handle *clh = ch->clh;
|
||||
struct dect_mbc_id id;
|
||||
enum dect_tbc_state state = 0;
|
||||
|
||||
if (!dect_ccp_parse_mbc_msg(&id, skb))
|
||||
return;
|
||||
clh->ops->mbc_conn_notify(clh, &id, state);
|
||||
}
|
||||
|
||||
static void dect_ccp_send_mbc_data_indicate(const struct dect_cluster_handle *clh,
|
||||
const struct dect_mbc_id *id,
|
||||
enum dect_data_channels chan,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
dect_ccp_build_mbc_msg(skb, id);
|
||||
dect_ccp_send_to_cluster(clh, skb, DECT_CCP_MBC_DATA_INDICATE);
|
||||
}
|
||||
|
||||
static void dect_ccp_parse_mbc_data_indicate(const struct dect_cell_handle *ch,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
const struct dect_cluster_handle *clh = ch->clh;
|
||||
struct dect_mbc_id id;
|
||||
|
||||
if (!dect_ccp_parse_mbc_msg(&id, skb))
|
||||
return;
|
||||
clh->ops->mbc_data_indicate(clh, &id, 0, skb); // FIXME
|
||||
}
|
||||
|
||||
static void dect_ccp_rcv_cell_msg(void *handle, u32 portref,
|
||||
struct sk_buff **pskb,
|
||||
const u8 *data, u32 size)
|
||||
{
|
||||
struct dect_cell_handle *ch = handle;
|
||||
struct dect_ccp_msg_hdr *h;
|
||||
struct sk_buff *skb = *pskb;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(*h)))
|
||||
return;
|
||||
h = (struct dect_ccp_msg_hdr *)skb->data;
|
||||
__skb_pull(skb, sizeof(*h));
|
||||
|
||||
switch (h->primitive) {
|
||||
case DECT_CCP_MBC_CONN_INDICATE:
|
||||
return dect_ccp_parse_mbc_conn_indicate(ch, skb);
|
||||
case DECT_CCP_MBC_CONN_NOTIFY:
|
||||
return dect_ccp_parse_mbc_conn_notify(ch, skb);
|
||||
case DECT_CCP_MBC_DATA_INDICATE:
|
||||
return dect_ccp_parse_mbc_data_indicate(ch, skb);
|
||||
}
|
||||
}
|
||||
|
||||
static void dect_ccp_cl_disconnect(void *handle, u32 portref,
|
||||
struct sk_buff **pskb,
|
||||
const u8 *data, u32 size, int reason)
|
||||
{
|
||||
struct dect_cell_handle *ch = handle;
|
||||
struct dect_cluster_handle *clh = ch->clh;
|
||||
|
||||
printk("cell disconnected\n");
|
||||
clh->ops->unbind(clh, ch);
|
||||
kfree(ch);
|
||||
}
|
||||
|
||||
static const struct dect_csf_ops dect_ccp_csf_ops = {
|
||||
.set_mode = dect_ccp_send_set_mode,
|
||||
.scan = dect_ccp_send_scan,
|
||||
.preload = dect_ccp_send_preload,
|
||||
.enable = dect_ccp_send_enable,
|
||||
.tbc_initiate = dect_ccp_send_tbc_initiate,
|
||||
.tbc_confirm = dect_ccp_send_tbc_confirm,
|
||||
.tbc_release = dect_ccp_send_tbc_release,
|
||||
};
|
||||
|
||||
static void dect_ccp_cl_named_msg(void *handle, u32 portref,
|
||||
struct sk_buff **pskb,
|
||||
const u8 *data, u32 size,
|
||||
u32 importance,
|
||||
const struct tipc_portid *source,
|
||||
const struct tipc_name_seq *dest)
|
||||
{
|
||||
struct dect_cluster *cl = handle;
|
||||
struct dect_cluster_handle *clh = &cl->handle;
|
||||
struct dect_cell_handle *ch;
|
||||
struct iovec ack = { NULL, 0};
|
||||
int err;
|
||||
|
||||
ch = kzalloc(sizeof(*ch), GFP_ATOMIC);
|
||||
if (ch == NULL)
|
||||
goto err1;
|
||||
ch->ops = &dect_ccp_csf_ops;
|
||||
|
||||
err = tipc_createport(cl->tipc_id, ch, TIPC_HIGH_IMPORTANCE,
|
||||
NULL, NULL, dect_ccp_cl_disconnect,
|
||||
NULL, NULL, dect_ccp_rcv_cell_msg, NULL,
|
||||
&ch->portref);
|
||||
if (err < 0)
|
||||
goto err2;
|
||||
|
||||
err = tipc_connect2port(ch->portref, source);
|
||||
if (err < 0)
|
||||
goto err3;
|
||||
|
||||
err = tipc_send(ch->portref, 1, &ack);
|
||||
if (err < 0)
|
||||
goto err3;
|
||||
|
||||
err = clh->ops->bind(clh, ch);
|
||||
if (err < 0)
|
||||
goto err4;
|
||||
return;
|
||||
|
||||
err4:
|
||||
tipc_disconnect(ch->portref);
|
||||
err3:
|
||||
tipc_deleteport(ch->portref);
|
||||
err2:
|
||||
kfree(ch);
|
||||
err1:
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* dect_ccp_cluster_init - Initialize a cluster control CCP instance
|
||||
*
|
||||
* @cl: DECT cluster
|
||||
*/
|
||||
int dect_ccp_cluster_init(struct dect_cluster *cl)
|
||||
{
|
||||
struct tipc_name_seq seq;
|
||||
int err;
|
||||
|
||||
err = tipc_attach(&cl->tipc_id, NULL, NULL);
|
||||
if (err < 0)
|
||||
goto err1;
|
||||
|
||||
err = tipc_createport(cl->tipc_id, cl, TIPC_HIGH_IMPORTANCE,
|
||||
NULL, NULL, NULL, NULL, dect_ccp_cl_named_msg,
|
||||
NULL, NULL, &cl->tipc_portref);
|
||||
if (err < 0)
|
||||
goto err2;
|
||||
|
||||
seq.type = DECT_CCP_TIPC_TYPE;
|
||||
seq.lower = DECT_CCP_CLUSTER_PORT_BASE + cl->index;
|
||||
seq.upper = DECT_CCP_CLUSTER_PORT_BASE + cl->index;
|
||||
err = tipc_publish(cl->tipc_portref, TIPC_CLUSTER_SCOPE, &seq);
|
||||
if (err < 0)
|
||||
goto err3;
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
tipc_deleteport(cl->tipc_portref);
|
||||
err2:
|
||||
tipc_detach(cl->tipc_id);
|
||||
err1:
|
||||
return err;
|
||||
}
|
||||
|
||||
void dect_ccp_cluster_shutdown(struct dect_cluster *cl)
|
||||
{
|
||||
tipc_detach(cl->tipc_id);
|
||||
}
|
||||
|
||||
static void dect_ccp_rcv_cluster_msg(void *handle, u32 portref,
|
||||
struct sk_buff **pskb,
|
||||
const u8 *data, u32 size)
|
||||
{
|
||||
struct sk_buff *skb = *pskb;
|
||||
struct dect_cell_handle *ch = handle;
|
||||
struct dect_ccp_msg_hdr *h;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(*h)))
|
||||
return;
|
||||
h = (struct dect_ccp_msg_hdr *)skb->data;
|
||||
__skb_pull(skb, sizeof(*h));
|
||||
|
||||
switch (h->primitive) {
|
||||
case DECT_CCP_SET_MODE:
|
||||
return dect_ccp_parse_set_mode(ch, skb);
|
||||
case DECT_CCP_SCAN:
|
||||
return dect_ccp_parse_scan(ch, skb);
|
||||
case DECT_CCP_ENABLE:
|
||||
return dect_ccp_parse_enable(ch, skb);
|
||||
case DECT_CCP_PRELOAD:
|
||||
return dect_ccp_parse_preload(ch, skb);
|
||||
case DECT_CCP_TBC_INITIATE:
|
||||
return dect_ccp_parse_tbc_initiate(ch, skb);
|
||||
case DECT_CCP_TBC_CONFIRM:
|
||||
return dect_ccp_parse_tbc_confirm(ch, skb);
|
||||
case DECT_CCP_TBC_RELEASE:
|
||||
return dect_ccp_parse_tbc_release(ch, skb);
|
||||
}
|
||||
}
|
||||
|
||||
static void dect_ccp_cluster_disconnect(void *handle, u32 portref,
|
||||
struct sk_buff **pskb,
|
||||
const u8 *data, u32 size, int reason)
|
||||
{
|
||||
printk("Cluster disconnected\n");
|
||||
#if 0
|
||||
struct dect_cell_handle *clh = handle;
|
||||
|
||||
clh->ops->unbind(clh);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void dect_ccp_subscr_rcv(void *handle, u32 portref,
|
||||
struct sk_buff **pskb,
|
||||
const u8 *data, u32 size)
|
||||
{
|
||||
struct dect_cell_handle *ch = handle;
|
||||
struct dect_cluster_handle *clh = ch->clh;
|
||||
struct sk_buff *skb = *pskb;
|
||||
struct tipc_event *ev;
|
||||
struct tipc_name name;
|
||||
int err;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(*ev)))
|
||||
return;
|
||||
ev = (struct tipc_event *)skb->data;
|
||||
|
||||
if (ev->event != TIPC_PUBLISHED)
|
||||
return;
|
||||
|
||||
/* Connect to cluster */
|
||||
err = tipc_createport(clh->tipc_id, ch, TIPC_HIGH_IMPORTANCE,
|
||||
NULL, NULL, dect_ccp_cluster_disconnect,
|
||||
NULL, NULL, dect_ccp_rcv_cluster_msg, NULL,
|
||||
&clh->portref);
|
||||
if (err < 0)
|
||||
goto err1;
|
||||
|
||||
name.type = DECT_CCP_TIPC_TYPE;
|
||||
name.instance = DECT_CCP_CLUSTER_PORT_BASE + clh->index;
|
||||
err = tipc_send2name(clh->portref, &name, 0, 0, NULL);
|
||||
if (err < 0)
|
||||
goto err2;
|
||||
return;
|
||||
|
||||
err2:
|
||||
tipc_deleteport(clh->portref);
|
||||
err1:
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* dect_ccp_cell_init - Initialize a cell CCP instance
|
||||
*
|
||||
* @cell: DECT cell
|
||||
*/
|
||||
static int dect_ccp_bind_cell(struct dect_cluster_handle *clh,
|
||||
struct dect_cell_handle *ch)
|
||||
{
|
||||
struct tipc_subscr subscr;
|
||||
struct iovec iov = { &subscr, sizeof(subscr) };
|
||||
struct tipc_name tname;
|
||||
int err;
|
||||
|
||||
err = tipc_attach(&clh->tipc_id, NULL, NULL);
|
||||
if (err < 0)
|
||||
goto err1;
|
||||
ch->clh = clh;
|
||||
|
||||
/* Connect to topology service and subscribe to cluster port */
|
||||
err = tipc_createport(clh->tipc_id, ch, TIPC_CRITICAL_IMPORTANCE,
|
||||
NULL, NULL, NULL, NULL, NULL,
|
||||
dect_ccp_subscr_rcv, NULL, &clh->tportref);
|
||||
if (err < 0)
|
||||
goto err2;
|
||||
|
||||
subscr.seq.type = DECT_CCP_TIPC_TYPE;
|
||||
subscr.seq.lower = DECT_CCP_CLUSTER_PORT_BASE + clh->index;
|
||||
subscr.seq.upper = DECT_CCP_CLUSTER_PORT_BASE + clh->index;
|
||||
subscr.timeout = TIPC_WAIT_FOREVER;
|
||||
subscr.filter = TIPC_SUB_PORTS;
|
||||
memset(&subscr.usr_handle, 0, sizeof(subscr.usr_handle));
|
||||
|
||||
tname.type = TIPC_TOP_SRV;
|
||||
tname.instance = TIPC_TOP_SRV;
|
||||
|
||||
err = tipc_send2name(clh->tportref, &tname, 0, 1, &iov);
|
||||
if (err < 0)
|
||||
goto err3;
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
tipc_deleteport(clh->tportref);
|
||||
err2:
|
||||
tipc_detach(clh->tipc_id);
|
||||
err1:
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
static void dect_ccp_unbind_cell(struct dect_cluster_handle *clh,
|
||||
struct dect_cell_handle *ch)
|
||||
{
|
||||
tipc_detach(clh->tipc_id);
|
||||
}
|
||||
|
||||
static void dect_ccp_send_bmc_page_indicate(const struct dect_cluster_handle *clh,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct dect_ccf_ops dect_ccp_ccf_ops = {
|
||||
.bind = dect_ccp_bind_cell,
|
||||
.unbind = dect_ccp_unbind_cell,
|
||||
.mbc_conn_indicate = dect_ccp_send_mbc_conn_indicate,
|
||||
.mbc_conn_notify = dect_ccp_send_mbc_conn_notify,
|
||||
.mbc_data_indicate = dect_ccp_send_mbc_data_indicate,
|
||||
.bmc_page_indicate = dect_ccp_send_bmc_page_indicate,
|
||||
};
|
||||
|
||||
struct dect_cluster_handle *dect_ccp_cell_init(struct dect_cell *cell, u8 clindex)
|
||||
{
|
||||
struct dect_cluster_handle *clh;
|
||||
|
||||
clh = kzalloc(sizeof(*clh), GFP_KERNEL);
|
||||
if (clh == NULL)
|
||||
return NULL;
|
||||
clh->index = clindex;
|
||||
clh->ops = &dect_ccp_ccf_ops;
|
||||
return clh;
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <net/dect/dect.h>
|
||||
#include <net/dect/transceiver.h>
|
||||
|
||||
static DEFINE_MUTEX(dect_cfg_mutex);
|
||||
|
||||
void dect_lock(void)
|
||||
{
|
||||
mutex_lock(&dect_cfg_mutex);
|
||||
}
|
||||
|
||||
void dect_unlock(void)
|
||||
{
|
||||
mutex_unlock(&dect_cfg_mutex);
|
||||
}
|
||||
|
||||
struct sk_buff *skb_append_frag(struct sk_buff *head, struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff **pprev;
|
||||
|
||||
if (head == NULL)
|
||||
return skb;
|
||||
|
||||
pprev = &skb_shinfo(head)->frag_list;
|
||||
while (*pprev != NULL)
|
||||
pprev = &(*pprev)->next;
|
||||
*pprev = skb;
|
||||
|
||||
head->data_len += skb->len;
|
||||
head->len += skb->len;
|
||||
head->truesize += skb->truesize;
|
||||
return head;
|
||||
}
|
||||
|
||||
static int __init dect_module_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = dect_transceiver_module_init();
|
||||
if (err < 0)
|
||||
goto err1;
|
||||
err = dect_netlink_module_init();
|
||||
if (err < 0)
|
||||
goto err2;
|
||||
err = dect_af_module_init();
|
||||
if (err < 0)
|
||||
goto err3;
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
dect_netlink_module_exit();
|
||||
err2:
|
||||
dect_af_module_exit();
|
||||
err1:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit dect_module_exit(void)
|
||||
{
|
||||
dect_af_module_exit();
|
||||
dect_netlink_module_exit();
|
||||
dect_transceiver_module_exit();
|
||||
}
|
||||
|
||||
module_init(dect_module_init);
|
||||
module_exit(dect_module_exit);
|
||||
|
||||
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
|
||||
MODULE_DESCRIPTION("DECT protocol stack");
|
||||
MODULE_LICENSE("GPL");
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* DECT DLC Layer
|
||||
*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define DEBUG
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/dect.h>
|
||||
#include <net/dect/dect.h>
|
||||
|
||||
static struct dect_mac_conn *
|
||||
dect_mac_conn_get_by_mcei(const struct dect_cluster *cl, u32 mcei)
|
||||
{
|
||||
struct dect_mac_conn *mc;
|
||||
|
||||
list_for_each_entry(mc, &cl->mac_connections, list) {
|
||||
if (mc->mcei == mcei)
|
||||
return mc;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dect_mac_conn *
|
||||
dect_mac_conn_get_by_mci(const struct dect_cluster *cl, const struct dect_mci *mci)
|
||||
{
|
||||
struct dect_mac_conn *mc;
|
||||
|
||||
list_for_each_entry(mc, &cl->mac_connections, list) {
|
||||
if (!dect_ari_cmp(&mc->mci.ari, &mci->ari) &&
|
||||
!dect_pmid_cmp(&mc->mci.pmid, &mci->pmid) &&
|
||||
mc->mci.lcn == mci->lcn)
|
||||
return mc;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dect_mac_conn *dect_mac_conn_init(struct dect_cluster *cl,
|
||||
const struct dect_mci *mci,
|
||||
const struct dect_mbc_id *id)
|
||||
{
|
||||
struct dect_mac_conn *mc;
|
||||
|
||||
mc = kzalloc(sizeof(*mc), GFP_ATOMIC);
|
||||
if (mc == NULL)
|
||||
return NULL;
|
||||
|
||||
mc->cl = cl;
|
||||
mc->mcei = id != NULL ? id->mcei : dect_mbc_alloc_mcei(cl);
|
||||
memcpy(&mc->mci, mci, sizeof(mc->mci));
|
||||
mc->state = DECT_MAC_CONN_CLOSED;
|
||||
|
||||
list_add_tail(&mc->list, &cl->mac_connections);
|
||||
return mc;
|
||||
}
|
||||
|
||||
void dect_dlc_mac_conn_release(struct dect_mac_conn *mc)
|
||||
{
|
||||
list_del(&mc->list);
|
||||
kfree(mc);
|
||||
}
|
||||
|
||||
static void dect_mac_conn_state_change(struct dect_mac_conn *mc,
|
||||
enum dect_mac_conn_states state)
|
||||
{
|
||||
mc->state = state;
|
||||
dect_cplane_notify_state_change(mc);
|
||||
}
|
||||
|
||||
int dect_dlc_mac_conn_establish(struct dect_mac_conn *mc)
|
||||
{
|
||||
struct dect_mbc_id mid = {
|
||||
.mcei = mc->mcei,
|
||||
.ari = mc->mci.ari,
|
||||
.pmid = mc->mci.pmid,
|
||||
.type = DECT_MAC_CONN_BASIC,
|
||||
.ecn = mc->mci.lcn,
|
||||
.service = mc->service,
|
||||
};
|
||||
int err;
|
||||
|
||||
err = dect_mbc_con_request(mc->cl, &mid);
|
||||
if (err < 0)
|
||||
return err;
|
||||
dect_mac_conn_state_change(mc, DECT_MAC_CONN_OPEN_PENDING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dect_dlc_mac_conn_confirm(struct dect_cluster *cl, u32 mcei,
|
||||
enum dect_mac_service_types service)
|
||||
{
|
||||
struct dect_mac_conn *mc;
|
||||
|
||||
mc = dect_mac_conn_get_by_mcei(cl, mcei);
|
||||
if (WARN_ON(mc == NULL))
|
||||
return -ENOENT;
|
||||
|
||||
mc->service = service;
|
||||
dect_mac_conn_state_change(mc, DECT_MAC_CONN_OPEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dect_dlc_mac_conn_indicate(struct dect_cluster *cl,
|
||||
const struct dect_mbc_id *id)
|
||||
{
|
||||
struct dect_mac_conn *mc;
|
||||
struct dect_mci mci = {
|
||||
.ari = id->ari,
|
||||
.pmid = id->pmid,
|
||||
.lcn = id->ecn & DECT_LCN_MASK,
|
||||
};
|
||||
|
||||
mc = dect_mac_conn_init(cl, &mci, id);
|
||||
if (mc == NULL)
|
||||
return -ENOMEM;
|
||||
mc->service = id->service;
|
||||
dect_mac_conn_state_change(mc, DECT_MAC_CONN_OPEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dect_dlc_mac_conn_disconnect(struct dect_cluster *cl, u32 mcei)
|
||||
{
|
||||
struct dect_mac_conn *mc;
|
||||
|
||||
mc = dect_mac_conn_get_by_mcei(cl, mcei);
|
||||
if (WARN_ON(mc == NULL))
|
||||
return -ENOENT;
|
||||
|
||||
dect_mac_conn_state_change(mc, DECT_MAC_CONN_CLOSED);
|
||||
dect_dlc_mac_conn_release(mc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dect_dlc_mac_co_data_indicate(struct dect_cluster *cl, u32 mcei,
|
||||
enum dect_data_channels chan,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct dect_mac_conn *mc;
|
||||
|
||||
mc = dect_mac_conn_get_by_mcei(cl, mcei);
|
||||
if (WARN_ON(mc == NULL))
|
||||
goto err;
|
||||
|
||||
pr_debug("dlc: data mcei %u mc %p chan %u len %u\n", mcei, mc, chan, skb->len);
|
||||
switch (chan) {
|
||||
case DECT_MC_C_S:
|
||||
case DECT_MC_C_F:
|
||||
return dect_cplane_rcv(mc, chan, skb);
|
||||
case DECT_MC_I_N:
|
||||
case DECT_MC_I_P:
|
||||
return dect_uplane_rcv(mc, chan, skb);
|
||||
default:
|
||||
goto err;
|
||||
}
|
||||
err:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
struct sk_buff *dect_dlc_mac_co_dtr_indicate(struct dect_cluster *cl, u32 mcei,
|
||||
enum dect_data_channels chan)
|
||||
{
|
||||
struct dect_mac_conn *mc;
|
||||
|
||||
mc = dect_mac_conn_get_by_mcei(cl, mcei);
|
||||
if (mc == NULL) {
|
||||
if (net_ratelimit())
|
||||
printk("DLC: DTR no connection\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pr_debug("dlc: dtr mcei %u mc %p chan %u\n", mcei, mc, chan);
|
||||
switch (chan) {
|
||||
case DECT_MC_C_S:
|
||||
case DECT_MC_C_F:
|
||||
return dect_cplane_dtr(mc, chan);
|
||||
case DECT_MC_I_N:
|
||||
case DECT_MC_I_P:
|
||||
return dect_uplane_dtr(mc, chan);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* DECT DLC B SAP sockets - DLC C-plane broadcast service access
|
||||
*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/dect.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/dect/dect.h>
|
||||
|
||||
static HLIST_HEAD(dect_bsap_sockets);
|
||||
|
||||
struct dect_bsap {
|
||||
struct sock sk;
|
||||
};
|
||||
|
||||
static inline struct dect_bsap *dect_bsap(struct sock *sk)
|
||||
{
|
||||
return (struct dect_bsap *)sk;
|
||||
}
|
||||
|
||||
void dect_bsap_rcv(const struct dect_cluster *cl, struct sk_buff *skb)
|
||||
{
|
||||
struct hlist_node *node;
|
||||
struct sk_buff *skb2;
|
||||
struct sock *sk;
|
||||
|
||||
sk_for_each(sk, node, &dect_bsap_sockets) {
|
||||
if (sk->sk_bound_dev_if &&
|
||||
sk->sk_bound_dev_if != cl->index)
|
||||
continue;
|
||||
|
||||
skb2 = skb_clone(skb, GFP_ATOMIC);
|
||||
if (skb2 == NULL) {
|
||||
sk->sk_err = -ENOMEM;
|
||||
sk->sk_error_report(sk);
|
||||
} else if (dect_sock_queue_rcv_skb(sk, skb2) < 0)
|
||||
kfree_skb(skb2);
|
||||
}
|
||||
}
|
||||
|
||||
static void dect_bsap_close(struct sock *sk, long timeout)
|
||||
{
|
||||
sk_del_node_init(sk);
|
||||
sock_put(sk);
|
||||
}
|
||||
|
||||
static int dect_bsap_bind(struct sock *sk, struct sockaddr *uaddr, int len)
|
||||
{
|
||||
const struct sockaddr_dect *addr = (struct sockaddr_dect *)uaddr;
|
||||
int err;
|
||||
|
||||
if (len < sizeof(*addr) || addr->dect_family != AF_DECT)
|
||||
return -EINVAL;
|
||||
|
||||
if (addr->dect_index != 0 &&
|
||||
!dect_cluster_get_by_index(addr->dect_index))
|
||||
return -ENODEV;
|
||||
|
||||
lock_sock(sk);
|
||||
err = -EINVAL;
|
||||
if (!sk_unhashed(sk))
|
||||
goto out;
|
||||
|
||||
sk->sk_bound_dev_if = addr->dect_index;
|
||||
sk_add_node(sk, &dect_bsap_sockets);
|
||||
err = 0;
|
||||
out:
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dect_bsap_getname(struct sock *sk, struct sockaddr *uaddr, int *len,
|
||||
int peer)
|
||||
{
|
||||
struct sockaddr_dect *addr = (struct sockaddr_dect *)uaddr;
|
||||
|
||||
if (peer)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
addr->dect_family = AF_DECT;
|
||||
addr->dect_index = sk->sk_bound_dev_if;
|
||||
*len = sizeof(*addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dect_bsap_recvmsg(struct kiocb *iocb, struct sock *sk,
|
||||
struct msghdr *msg, size_t len,
|
||||
int noblock, int flags, int *addrlen)
|
||||
{
|
||||
struct sockaddr_dect *addr;
|
||||
struct sk_buff *skb;
|
||||
size_t copied = 0;
|
||||
int err;
|
||||
|
||||
if (flags & MSG_OOB)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
noblock = flags & MSG_DONTWAIT;
|
||||
skb = skb_recv_datagram(sk, flags, noblock, &err);
|
||||
if (skb == NULL)
|
||||
goto out;
|
||||
|
||||
//msg->msg_flags |= DECT_LB_CB(skb)->expedited ? MSG_OOB : 0;
|
||||
|
||||
copied = skb->len;
|
||||
if (len < copied) {
|
||||
msg->msg_flags |= MSG_TRUNC;
|
||||
copied = len;
|
||||
}
|
||||
|
||||
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
|
||||
if (msg->msg_name != NULL) {
|
||||
addr = (struct sockaddr_dect *)msg->msg_name;
|
||||
addr->dect_family = AF_DECT;
|
||||
addr->dect_index = DECT_SK_CB(skb)->index;
|
||||
msg->msg_namelen = sizeof(*addr);
|
||||
}
|
||||
|
||||
sock_recv_timestamp(msg, sk, skb);
|
||||
|
||||
if (flags & MSG_TRUNC)
|
||||
copied = skb->len;
|
||||
out_free:
|
||||
skb_free_datagram(sk, skb);
|
||||
out:
|
||||
return err ? : copied;
|
||||
}
|
||||
|
||||
static int dect_bsap_sendmsg(struct kiocb *kiocb, struct sock *sk,
|
||||
struct msghdr *msg, size_t len)
|
||||
{
|
||||
const struct sockaddr_dect *addr = msg->msg_name;
|
||||
bool expedited = msg->msg_flags & MSG_OOB;
|
||||
struct dect_cluster *cl;
|
||||
struct sk_buff *skb;
|
||||
int index;
|
||||
int err;
|
||||
|
||||
if (msg->msg_namelen) {
|
||||
if (addr->dect_family != AF_DECT)
|
||||
return -EINVAL;
|
||||
index = addr->dect_index;
|
||||
} else
|
||||
index = sk->sk_bound_dev_if;
|
||||
|
||||
/* Transmission is always in direction FP -> PP */
|
||||
cl = dect_cluster_get_by_index(index);
|
||||
if (cl == NULL)
|
||||
return -ENODEV;
|
||||
if (cl->mode != DECT_MODE_FP)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Valid frame sizes are 3 bytes (short frame), 5 bytes (long frame)
|
||||
* or multiples of 5 bytes up to 30 bytes (extended frame). Extended
|
||||
* frames can not use expedited operation. */
|
||||
if (len != DECT_LB_SHORT_FRAME_SIZE &&
|
||||
len != DECT_LB_LONG_FRAME_SIZE) {
|
||||
if (len % DECT_LB_LONG_FRAME_SIZE != 0)
|
||||
return -EINVAL;
|
||||
if (len > DECT_LB_EXTENDED_FRAME_SIZE_MAX)
|
||||
return -EMSGSIZE;
|
||||
if (expedited)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
skb = sock_alloc_send_skb(sk, len, msg->msg_flags & MSG_DONTWAIT, &err);
|
||||
if (skb == NULL)
|
||||
goto err1;
|
||||
err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
|
||||
if (err < 0)
|
||||
goto err2;
|
||||
dect_bmc_mac_page_request(cl, skb, expedited);
|
||||
return len;
|
||||
|
||||
err2:
|
||||
kfree_skb(skb);
|
||||
err1:
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct dect_proto dect_bsap_proto __read_mostly = {
|
||||
.type = SOCK_DGRAM,
|
||||
.protocol = DECT_B_SAP,
|
||||
.capability = CAP_NET_RAW,
|
||||
.ops = &dect_dgram_ops,
|
||||
.proto.name = "DECT_B_SAP",
|
||||
.proto.owner = THIS_MODULE,
|
||||
.proto.obj_size = sizeof(struct dect_bsap),
|
||||
.proto.close = dect_bsap_close,
|
||||
.proto.bind = dect_bsap_bind,
|
||||
.proto.recvmsg = dect_bsap_recvmsg,
|
||||
.proto.sendmsg = dect_bsap_sendmsg,
|
||||
.getname = dect_bsap_getname,
|
||||
};
|
||||
|
||||
int __init dect_bsap_module_init(void)
|
||||
{
|
||||
return dect_proto_register(&dect_bsap_proto);
|
||||
}
|
||||
|
||||
void dect_bsap_module_exit(void)
|
||||
{
|
||||
dect_proto_unregister(&dect_bsap_proto);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
|
||||
MODULE_DESCRIPTION("DECT DLC B SAP sockets");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
MODULE_ALIAS_NET_PF_PROTO(PF_DECT, DECT_B_SAP);
|
|
@ -0,0 +1,823 @@
|
|||
/*
|
||||
* DECT DLC C-plane
|
||||
*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define DEBUG
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/dect.h>
|
||||
#include <net/dect/dect.h>
|
||||
|
||||
void dect_dlc_mac_page_indicate(struct dect_cluster *cl, struct sk_buff *skb)
|
||||
{
|
||||
dect_bsap_rcv(cl, skb);
|
||||
}
|
||||
|
||||
static void dect_fa_parse_len(struct dect_fa_len *len, const struct sk_buff *skb)
|
||||
{
|
||||
u8 l;
|
||||
|
||||
l = skb->data[DECT_FA_LI_OFF];
|
||||
len->len = (l & DECT_FA_LI_LENGTH_MASK) >> DECT_FA_LI_LENGTH_SHIFT;
|
||||
len->more = (l & DECT_FA_LI_M_FLAG);
|
||||
}
|
||||
|
||||
/*
|
||||
* LAPC entity
|
||||
*/
|
||||
|
||||
#define lapc_debug(lapc, fmt, args...) \
|
||||
pr_debug("LAPC (MCEI: %u LLN: %u): " fmt, \
|
||||
(lapc)->lc->mc->mcei, (lapc)->dli.lln, ## args)
|
||||
|
||||
static inline u8 lapc_seq_add(const struct dect_lapc *lapc, u8 s1, u8 s2)
|
||||
{
|
||||
return (s1 + s2) & (lapc->mod - 1);
|
||||
}
|
||||
|
||||
static inline bool dect_fa_seq_before(const struct dect_lapc *lapc, u8 s1, u8 s2)
|
||||
{
|
||||
if (lapc->window == 1)
|
||||
return s1 != s2;
|
||||
else
|
||||
return (s8)((s2 << 5) - (s1 << 5)) > 0;
|
||||
}
|
||||
|
||||
static inline bool dect_fa_seq_after(const struct dect_lapc *lapc, u8 s1, u8 s2)
|
||||
{
|
||||
return dect_fa_seq_before(lapc, s2, s1);
|
||||
}
|
||||
|
||||
/**
|
||||
* dect_lapc_timeout - retransmission timer
|
||||
*
|
||||
* Handle missing acknowledgements:
|
||||
*
|
||||
* - If not already in timer recovery condition, enter it
|
||||
* - otherwise add one to retransmission count
|
||||
*
|
||||
* If the retransmission count is below the maximum, restart the timer and
|
||||
* send an "appropriate" S-frame acknowledgement or retransmit the last
|
||||
* I-frame, in both cases with the poll bit set.
|
||||
*/
|
||||
static void dect_lapc_timeout(unsigned long data)
|
||||
{
|
||||
struct dect_lapc *lapc = (struct dect_lapc *)data;
|
||||
struct sock *sk;
|
||||
|
||||
lapc_debug(lapc, "retransmission timer: cnt: %u\n", lapc->rcnt);
|
||||
if (lapc->rcnt++ < DECT_LAPC_RETRANS_MAX) {
|
||||
mod_timer(&lapc->timer, jiffies + DECT_LAPC_CLASS_A_ESTABLISH_TIMEOUT);
|
||||
} else {
|
||||
sk = lapc->sk;
|
||||
sk->sk_err = ETIMEDOUT;
|
||||
sk->sk_error_report(sk);
|
||||
}
|
||||
}
|
||||
|
||||
void dect_lapc_release(struct dect_lapc *lapc)
|
||||
{
|
||||
lapc_debug(lapc, "release\n");
|
||||
|
||||
if (lapc->lc->lapcs[lapc->dli.lln] != NULL)
|
||||
lapc->lc->lapcs[lapc->dli.lln] = NULL;
|
||||
del_timer_sync(&lapc->timer);
|
||||
kfree(lapc);
|
||||
}
|
||||
|
||||
/**
|
||||
* dect_lapc_init - initialize a new LAPC entity
|
||||
*/
|
||||
struct dect_lapc *dect_lapc_init(const struct dect_dli *dli,
|
||||
enum dect_sapis sapi,
|
||||
struct dect_lc *lc, gfp_t gfp)
|
||||
{
|
||||
struct dect_lapc *lapc;
|
||||
|
||||
lapc = kzalloc(sizeof(*lapc), gfp);
|
||||
if (lapc == NULL)
|
||||
return NULL;
|
||||
|
||||
memcpy(&lapc->dli, dli, sizeof(lapc->dli));
|
||||
lapc->sapi = sapi;
|
||||
lapc->state = DECT_LAPC_ULI;
|
||||
skb_queue_head_init(&lapc->retransq);
|
||||
|
||||
lapc->lc = lc;
|
||||
setup_timer(&lapc->timer, dect_lapc_timeout, (unsigned long)lapc);
|
||||
lapc->nlf = true;
|
||||
lapc->cmd = (lc->mc->cl->mode == DECT_MODE_FP) ? true : false;
|
||||
|
||||
switch (lapc->dli.lln) {
|
||||
case DECT_LLN_CLASS_U:
|
||||
break;
|
||||
case DECT_LLN_CLASS_A:
|
||||
lapc->window = DECT_LAPC_CLASS_A_WINDOW;
|
||||
lapc->mod = DECT_LAPC_CLASS_A_MOD;
|
||||
break;
|
||||
default:
|
||||
lapc->window = DECT_LAPC_CLASS_B_INITIAL_WINDOW;
|
||||
lapc->mod = DECT_LAPC_CLASS_B_MOD;
|
||||
break;
|
||||
}
|
||||
|
||||
lapc_debug(lapc, "init\n");
|
||||
return lapc;
|
||||
}
|
||||
|
||||
#define DECT_FA_FRAME_RESERVE 16
|
||||
#define DECT_FA_FRAME_SPACE 16
|
||||
|
||||
static struct sk_buff *dect_lapc_alloc_skb(struct dect_lapc *lapc)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = alloc_skb(DECT_FA_FRAME_SPACE + DECT_FA_FRAME_RESERVE, GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
return NULL;
|
||||
skb_reset_mac_header(skb);
|
||||
skb_reserve(skb, DECT_FA_FRAME_RESERVE);
|
||||
skb_reserve(skb, DECT_FA_HDR_SIZE);
|
||||
skb_reset_network_header(skb);
|
||||
return skb;
|
||||
}
|
||||
|
||||
static struct dect_fa_hdr *dect_prepare_fa_frame(const struct dect_lapc *lapc,
|
||||
bool command,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct dect_fa_hdr *fh;
|
||||
u8 ilen = skb->len;
|
||||
|
||||
fh = (struct dect_fa_hdr *)skb_push(skb, DECT_FA_HDR_SIZE);
|
||||
fh->addr = lapc->dli.lln << DECT_FA_ADDR_LLN_SHIFT;
|
||||
fh->addr |= lapc->sapi << DECT_FA_ADDR_SAPI_SHIFT;
|
||||
fh->addr |= DECT_FA_ADDR_RES_BIT;
|
||||
fh->addr |= (command ? lapc->cmd : !lapc->cmd) ? DECT_FA_ADDR_CR_FLAG : 0;
|
||||
fh->addr |= lapc->nlf ? DECT_FA_ADDR_NLF_FLAG : 0;
|
||||
fh->ctrl = 0;
|
||||
fh->li = ilen << DECT_FA_LI_LENGTH_SHIFT;
|
||||
fh->li |= DECT_FA_LI_EXT_FLAG;
|
||||
return fh;
|
||||
}
|
||||
|
||||
static bool dect_lapc_send_iframe(struct dect_lapc *lapc, bool pf)
|
||||
{
|
||||
struct dect_fa_hdr *fh;
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* Window size full? */
|
||||
lapc_debug(lapc, "send iframe v_a: %u window: %u v_s: %u\n",
|
||||
lapc->v_a, lapc->window, lapc->v_s);
|
||||
if (lapc_seq_add(lapc, lapc->v_a, lapc->window) == lapc->v_s)
|
||||
return false;
|
||||
|
||||
/* Prepare a new I-frame */
|
||||
skb = skb_dequeue(&lapc->sk->sk_write_queue);
|
||||
if (skb == NULL)
|
||||
return false;
|
||||
fh = dect_prepare_fa_frame(lapc, true, skb);
|
||||
fh->ctrl |= DECT_FA_CTRL_I_FMT_ID;
|
||||
fh->ctrl |= lapc->v_r << DECT_FA_CTRL_I_NR_SHIFT;
|
||||
fh->ctrl |= lapc->v_s << DECT_FA_CTRL_I_NS_SHIFT;
|
||||
fh->ctrl |= pf ? DECT_FA_CTRL_I_P_FLAG : 0;
|
||||
|
||||
/* Append to retransmission queue and (re)start retransmission timer */
|
||||
skb_queue_tail(&lapc->retransq, skb);
|
||||
if (!timer_pending(&lapc->timer))
|
||||
mod_timer(&lapc->timer, jiffies + DECT_LAPC_RETRANSMISSION_TIMEOUT);
|
||||
|
||||
lapc->v_s = lapc_seq_add(lapc, lapc->v_s, 1);
|
||||
|
||||
skb = skb_clone(skb, GFP_ATOMIC);
|
||||
if (skb == NULL) {
|
||||
/* Will be retransmitted */
|
||||
return true;
|
||||
}
|
||||
|
||||
lapc_debug(lapc, "queue I-frame v_a: %u v_r: %u v_s: %u len: %u addr: %.2x ctrl: %.2x\n",
|
||||
lapc->v_a, lapc->v_r, lapc->v_s, skb->len, fh->addr, fh->ctrl);
|
||||
skb_queue_tail(&lapc->lc->txq, skb);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a S-frame with the specified command. The command/response bit setting
|
||||
* depends on the role of the LAPC, a PP uses 0 for commands and 1 for responses,
|
||||
* a FT 1 for commands and 0 for responses.
|
||||
*/
|
||||
static bool dect_lapc_send_sframe(struct dect_lapc *lapc, u8 cr,
|
||||
bool command, bool pf)
|
||||
{
|
||||
struct dect_fa_hdr *fh;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = dect_lapc_alloc_skb(lapc);
|
||||
if (skb == NULL)
|
||||
return false;
|
||||
|
||||
fh = dect_prepare_fa_frame(lapc, command, skb);
|
||||
fh->ctrl |= DECT_FA_CTRL_S_FMT_ID;
|
||||
fh->ctrl |= lapc->v_r << DECT_FA_CTRL_S_NR_SHIFT;
|
||||
fh->ctrl |= cr;
|
||||
fh->ctrl |= pf ? DECT_FA_CTRL_S_PF_FLAG : 0;
|
||||
|
||||
lapc_debug(lapc, "queue S-frame v_r: %u len: %u addr: %.2x ctrl: %.2x\n",
|
||||
lapc->v_r, skb->len, fh->addr, fh->ctrl);
|
||||
skb_queue_tail(&lapc->lc->txq, skb);
|
||||
|
||||
lapc->nlf = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send an acknowledgement frame. Class B entities use RNR responses to indicate
|
||||
* their status while busy. Otherwise an I-frame is used when data is available
|
||||
* and a RR response frame otherwise.
|
||||
*/
|
||||
static void dect_lapc_send_ack(struct dect_lapc *lapc, bool pf)
|
||||
{
|
||||
lapc_debug(lapc, "send ACK I-frame present: %u\n",
|
||||
skb_peek(&lapc->sk->sk_write_queue) ? 1 : 0);
|
||||
if (lapc->dli.lln != DECT_LLN_CLASS_A && lapc->busy)
|
||||
dect_lapc_send_sframe(lapc, DECT_FA_CTRL_S_CR_RNR, false, false);
|
||||
else if (!lapc->peer_busy && skb_peek(&lapc->sk->sk_write_queue))
|
||||
dect_lapc_send_iframe(lapc, pf);
|
||||
else
|
||||
dect_lapc_send_sframe(lapc, DECT_FA_CTRL_S_CR_RR, false, pf);
|
||||
}
|
||||
|
||||
static void dect_lapc_queue_data(struct dect_lapc *lapc, struct sk_buff *skb)
|
||||
{
|
||||
struct dect_fa_hdr *fh = (struct dect_fa_hdr *)skb->data;
|
||||
|
||||
skb_pull(skb, DECT_FA_HDR_SIZE);
|
||||
if (skb->len == 0) {
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
lapc_debug(lapc, "reassemble: segment len %u more %u\n",
|
||||
skb->len, (fh->li & DECT_FA_LI_M_FLAG) ? 1 : 0);
|
||||
|
||||
lapc->rcv_head = skb_append_frag(lapc->rcv_head, skb);
|
||||
if (!(fh->li & DECT_FA_LI_M_FLAG)) {
|
||||
skb = lapc->rcv_head;
|
||||
lapc->rcv_head = NULL;
|
||||
lapc_debug(lapc, "reassembled: message len %u\n", skb->len);
|
||||
sock_queue_rcv_skb(lapc->sk, skb);
|
||||
}
|
||||
}
|
||||
|
||||
static bool dect_lapc_update_ack(struct dect_lapc *lapc, u8 seq)
|
||||
{
|
||||
u8 v_a = lapc->v_a;
|
||||
|
||||
lapc_debug(lapc, "update ACK: v_a: %u v_s: %u seq: %u\n",
|
||||
lapc->v_a, lapc->v_s, seq);
|
||||
lapc_debug(lapc, "seq %u after v_a %u: %u\n", seq, lapc->v_a,
|
||||
dect_fa_seq_after(lapc, seq, lapc->v_a));
|
||||
lapc_debug(lapc, "v_s %u !after seq %u: %u\n", lapc->v_s, seq,
|
||||
!dect_fa_seq_after(lapc, lapc->v_s, seq));
|
||||
|
||||
/* If all outstanding I-frames have been acknowledged, stop
|
||||
* retransmission timer, otherwise reset it.
|
||||
*/
|
||||
if (dect_fa_seq_after(lapc, seq, lapc->v_a) &&
|
||||
!dect_fa_seq_after(lapc, lapc->v_s, seq)) {
|
||||
lapc->v_a = seq;
|
||||
if (lapc->v_a == lapc->v_s)
|
||||
del_timer_sync(&lapc->timer);
|
||||
else
|
||||
mod_timer(&lapc->timer, jiffies + DECT_LAPC_RETRANSMISSION_TIMEOUT);
|
||||
} else if (seq != lapc->v_a)
|
||||
return false;
|
||||
|
||||
/* Purge acknowledged frames from transmit queue */
|
||||
while (v_a != lapc->v_a) {
|
||||
lapc_debug(lapc, "purge retransmit queue seq: %u\n", v_a);
|
||||
kfree_skb(skb_dequeue(&lapc->retransq));
|
||||
v_a = lapc_seq_add(lapc, v_a, 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Receive a Class A or Class B I-frame. Frames with valid sequence numbers
|
||||
* are acknowledged and queued for segment reassembly. Invalid sequence
|
||||
* numbers cause an ACK with the expected sequence number to be sent.
|
||||
*
|
||||
* Class B entities need to indicate their receiver busy status when busy or
|
||||
* when explicitly polled.
|
||||
*/
|
||||
static void dect_lapc_rcv_iframe(struct dect_lapc *lapc, struct sk_buff *skb)
|
||||
{
|
||||
struct dect_fa_hdr *fh = (struct dect_fa_hdr *)skb->data;
|
||||
bool poll = false;
|
||||
u8 n_s, n_r, res;
|
||||
|
||||
if (lapc->dli.lln == DECT_LLN_CLASS_U) {
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
n_r = (fh->ctrl & DECT_FA_CTRL_I_NR_MASK) >> DECT_FA_CTRL_I_NR_SHIFT;
|
||||
n_s = (fh->ctrl & DECT_FA_CTRL_I_NS_MASK) >> DECT_FA_CTRL_I_NS_SHIFT;
|
||||
if (lapc->dli.lln != DECT_LLN_CLASS_A)
|
||||
poll = fh->ctrl & DECT_FA_CTRL_I_P_FLAG;
|
||||
|
||||
lapc_debug(lapc, "receive I-frame: n_r: %u n_s: %u poll: %u\n",
|
||||
n_r, n_s, poll);
|
||||
dect_lapc_update_ack(lapc, n_r);
|
||||
|
||||
/* While in receiver busy condition, all I-frames are dropped after
|
||||
* updating the acknowledgement number. In Class B mode receiver status
|
||||
* queries are still answered.
|
||||
*/
|
||||
if (lapc->busy) {
|
||||
kfree_skb(skb);
|
||||
if (poll)
|
||||
goto poll;
|
||||
return;
|
||||
}
|
||||
|
||||
/* When the frame contains an invalid sequence number, send an
|
||||
* immediate ACK. */
|
||||
if (n_s != lapc->v_r) {
|
||||
lapc_debug(lapc, "invalid sequence number %u %u\n", n_s, lapc->v_r);
|
||||
kfree_skb(skb);
|
||||
goto ack;
|
||||
}
|
||||
|
||||
lapc->v_r = lapc_seq_add(lapc, lapc->v_r, 1);
|
||||
dect_lapc_queue_data(lapc, skb);
|
||||
if (poll)
|
||||
goto poll;
|
||||
ack:
|
||||
return dect_lapc_send_ack(lapc, poll);
|
||||
|
||||
poll:
|
||||
res = lapc->busy ? DECT_FA_CTRL_S_CR_RNR : DECT_FA_CTRL_S_CR_RR;
|
||||
dect_lapc_send_sframe(lapc, res, false, true);
|
||||
}
|
||||
|
||||
static void dect_lapc_rcv_sframe(struct dect_lapc *lapc, struct sk_buff *skb)
|
||||
{
|
||||
struct dect_fa_hdr *fh = (struct dect_fa_hdr *)skb->data;
|
||||
bool pf;
|
||||
u8 n_r;
|
||||
|
||||
n_r = (fh->ctrl & DECT_FA_CTRL_S_NR_MASK) >> DECT_FA_CTRL_S_NR_SHIFT;
|
||||
pf = (fh->ctrl & DECT_FA_CTRL_S_PF_FLAG);
|
||||
lapc_debug(lapc, "receive S-frame: n_r: %u pf: %u\n", n_r, pf);
|
||||
|
||||
switch (fh->ctrl & DECT_FA_CTRL_S_CR_MASK) {
|
||||
case DECT_FA_CTRL_S_CR_RR:
|
||||
if (!dect_lapc_update_ack(lapc, n_r))
|
||||
goto err;
|
||||
|
||||
if (lapc->lc->elapc == lapc) {
|
||||
/* Connection establishment completed */
|
||||
lapc_debug(lapc, "established\n");
|
||||
lapc->lc->elapc = NULL;
|
||||
del_timer_sync(&lapc->timer);
|
||||
|
||||
lapc->sk->sk_state = DECT_SK_ESTABLISHED;
|
||||
lapc->sk->sk_state_change(lapc->sk);
|
||||
}
|
||||
|
||||
dect_lapc_send_iframe(lapc, pf);
|
||||
break;
|
||||
case DECT_FA_CTRL_S_CR_RNR:
|
||||
/*
|
||||
* Note peer receiver busy condition. If it was a RNR command
|
||||
* with the P bit set to 1, send a RR response with the F bit
|
||||
* set to 1. If it was a RNR response with the F bit set to 1,
|
||||
* clear timer recovery condition and update V(S).
|
||||
*/
|
||||
lapc->peer_busy = true;
|
||||
|
||||
if (fh->addr & DECT_FA_ADDR_CR_FLAG && pf)
|
||||
dect_lapc_send_sframe(lapc, DECT_FA_CTRL_S_CR_RR, true, true);
|
||||
else if (!(fh->addr & DECT_FA_ADDR_CR_FLAG) && pf) {
|
||||
del_timer_sync(&lapc->timer);
|
||||
lapc->v_s = n_r;
|
||||
}
|
||||
|
||||
dect_lapc_update_ack(lapc, n_r);
|
||||
break;
|
||||
case DECT_FA_CTRL_S_CR_REJ:
|
||||
lapc->peer_busy = false;
|
||||
lapc->v_s = n_r;
|
||||
lapc->v_a = n_r;
|
||||
del_timer_sync(&lapc->timer);
|
||||
break;
|
||||
default:
|
||||
goto err;
|
||||
}
|
||||
|
||||
err:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static void dect_lapc_rcv_uframe(struct dect_lapc *lapc, struct sk_buff *skb)
|
||||
{
|
||||
struct dect_fa_hdr *fh = (struct dect_fa_hdr *)skb->data;
|
||||
u8 pf, cr;
|
||||
|
||||
pf = (fh->ctrl & DECT_FA_CTRL_U_PF_FLAG);
|
||||
cr = (fh->ctrl & DECT_FA_CTRL_U_U1_MASK) |
|
||||
(fh->ctrl & DECT_FA_CTRL_U_CR_MASK);
|
||||
|
||||
/* unnumbered information is only valid in class U mode */
|
||||
if (cr == DECT_FA_CTRL_U_CR_UI) {
|
||||
if (lapc->dli.lln != DECT_LLN_CLASS_U)
|
||||
goto err;
|
||||
lapc_debug(lapc, "queue UI message len: %u\n", skb->len);
|
||||
sock_queue_rcv_skb(lapc->sk, skb);
|
||||
return;
|
||||
}
|
||||
|
||||
/* the remaining commands/responses are only valid in class B mode */
|
||||
if (lapc->dli.lln == DECT_LLN_CLASS_A)
|
||||
goto err;
|
||||
|
||||
switch (cr) {
|
||||
case DECT_FA_CTRL_U_CR_SABM:
|
||||
break;
|
||||
case DECT_FA_CTRL_U_CR_DM:
|
||||
break;
|
||||
case DECT_FA_CTRL_U_CR_DISC:
|
||||
break;
|
||||
case DECT_FA_CTRL_U_CR_UA:
|
||||
break;
|
||||
}
|
||||
|
||||
err:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static void dect_lapc_rcv(struct dect_lapc *lapc, struct sk_buff *skb)
|
||||
{
|
||||
struct dect_fa_hdr *fh = (struct dect_fa_hdr *)skb->data;
|
||||
|
||||
if ((fh->ctrl & DECT_FA_CTRL_I_FMT_MASK) == DECT_FA_CTRL_I_FMT_ID)
|
||||
return dect_lapc_rcv_iframe(lapc, skb);
|
||||
else if ((fh->ctrl & DECT_FA_CTRL_S_FMT_MASK) == DECT_FA_CTRL_S_FMT_ID)
|
||||
return dect_lapc_rcv_sframe(lapc, skb);
|
||||
else if ((fh->ctrl & DECT_FA_CTRL_U_FMT_MASK) == DECT_FA_CTRL_U_FMT_ID)
|
||||
return dect_lapc_rcv_uframe(lapc, skb);
|
||||
else
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
int dect_lapc_transmit(struct dect_lapc *lapc)
|
||||
{
|
||||
dect_lapc_send_iframe(lapc, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dect_lapc_establish(struct dect_lapc *lapc)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
lapc_debug(lapc, "establish\n");
|
||||
|
||||
/* Prepend zero-sized message to transmit queue to trigger connection
|
||||
* establishment.
|
||||
*/
|
||||
skb = dect_lapc_alloc_skb(lapc);
|
||||
if (skb == NULL)
|
||||
return -ENOMEM;
|
||||
skb_queue_head(&lapc->sk->sk_write_queue, skb);
|
||||
|
||||
lapc->lc->elapc = lapc;
|
||||
dect_lapc_send_iframe(lapc, lapc->dli.lln != DECT_LLN_CLASS_A);
|
||||
lapc->nlf = false;
|
||||
|
||||
mod_timer(&lapc->timer, jiffies + DECT_LAPC_CLASS_A_ESTABLISH_TIMEOUT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lc entity
|
||||
*
|
||||
* The Lc entity receives and transmits LAPC frames from/to the MAC layer.
|
||||
*
|
||||
* For transmission the frames are checksummed and fragmented into channel
|
||||
* sized units. The channel is chosen before transmission of a new frame
|
||||
* based on availability and demand. All fragments of one frame are
|
||||
* transmitted in the chosen channel.
|
||||
*
|
||||
* Received fragments are resegmented and have their checksum validated,
|
||||
* then routed to the LAPC entity associated with the logical link number.
|
||||
*/
|
||||
|
||||
#define lc_debug(lc, fmt, args...) \
|
||||
pr_debug("Lc (MCEI %x): " fmt, (lc)->mc->mcei, ## args)
|
||||
|
||||
void dect_lc_release(struct dect_lc *lc)
|
||||
{
|
||||
kfree_skb(lc->rx_head);
|
||||
kfree_skb(lc->tx_head);
|
||||
__skb_queue_purge(&lc->txq);
|
||||
kfree(lc);
|
||||
}
|
||||
|
||||
struct dect_lc *dect_lc_init(struct dect_mac_conn *mc, gfp_t gfp)
|
||||
{
|
||||
struct dect_lc *lc;
|
||||
|
||||
lc = kzalloc(sizeof(*lc), gfp);
|
||||
if (lc == NULL)
|
||||
return NULL;
|
||||
lc->mc = mc;
|
||||
lc_debug(lc, "init\n");
|
||||
|
||||
skb_queue_head_init(&lc->txq);
|
||||
switch (mc->mci.pmid.type) {
|
||||
case DECT_PMID_ASSIGNED:
|
||||
lc->lsig = htons(dect_build_pmid(&mc->mci.pmid) >> 4);
|
||||
break;
|
||||
default:
|
||||
lc->lsig = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return lc;
|
||||
}
|
||||
|
||||
static void dect_fa_frame_csum(const struct dect_lc *lc, struct sk_buff *skb)
|
||||
{
|
||||
u8 *data = skb->data;
|
||||
unsigned int i;
|
||||
u8 c0 = 0, c1 = 0;
|
||||
u8 x, y;
|
||||
u16 t;
|
||||
|
||||
data[skb->len - 2] = 0;
|
||||
data[skb->len - 1] = 0;
|
||||
|
||||
for (i = 0; i < skb->len; i++) {
|
||||
t = c0 + data[i];
|
||||
c0 = (t & 0xffU) + ((t >> 8) & 0x1U);
|
||||
t = c1 + c0;
|
||||
c1 = (t & 0xffU) + ((t >> 8) & 0x1U);
|
||||
}
|
||||
|
||||
t = c0 + (u8)~c1;
|
||||
x = (t & 0xffU) + ((t >> 8) & 0x1U);
|
||||
|
||||
t = (u8)~c0 + (u8)~c0;
|
||||
t = (t & 0xffU) + ((t >> 8) & 0x1U);
|
||||
t += c1;
|
||||
y = (t & 0xffU) + ((t >> 8) & 0x1U);
|
||||
|
||||
data[skb->len - 2] = x ^ (lc->lsig >> 8);
|
||||
data[skb->len - 1] = y ^ (lc->lsig & 0xff);
|
||||
lc_debug(lc, "checksum: lsig: %.4x x: %.2x y: %.2x\n",
|
||||
lc->lsig, x, y);
|
||||
}
|
||||
|
||||
static bool dect_fa_frame_csum_verify(const struct dect_lc *lc,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
u8 *data = skb->data;
|
||||
unsigned int i;
|
||||
u8 c0 = 0, c1 = 0;
|
||||
u16 t;
|
||||
|
||||
data[skb->len - 2] ^= lc->lsig >> 8;
|
||||
data[skb->len - 1] ^= lc->lsig & 0xff;
|
||||
|
||||
for (i = 0; i < skb->len; i++) {
|
||||
t = c0 + data[i];
|
||||
c0 = (t & 0xffU) + ((t >> 8) & 0x1U);
|
||||
t = c1 + c0;
|
||||
c1 = (t & 0xffU) + ((t >> 8) & 0x1U);
|
||||
}
|
||||
|
||||
lc_debug(lc, "csum verify: lsig %.4x c0: %.2x c1: %.2x\n",
|
||||
lc->lsig, c0, c1);
|
||||
return c0 == (u8)~0 && c1 == (u8)~0;
|
||||
}
|
||||
|
||||
static const u8 channel_sdu_size[] = {
|
||||
[DECT_MC_C_S] = DECT_C_S_SDU_SIZE,
|
||||
[DECT_MC_C_F] = DECT_C_F_SDU_SIZE,
|
||||
};
|
||||
|
||||
/*
|
||||
* Prepare a DLC frame for transmission to the MAC layer. This involves
|
||||
* checksumming the frame, selecting the logical channel for transmission
|
||||
* and fragmenting it into units carried by the logical channel.
|
||||
*/
|
||||
static struct sk_buff *dect_lc_tx(struct dect_lc *lc)
|
||||
{
|
||||
struct sk_buff *skb, *frag;
|
||||
u8 *fill, fill_len;
|
||||
u8 flen;
|
||||
|
||||
skb = lc->tx_head;
|
||||
if (skb == NULL) {
|
||||
skb = skb_dequeue(&lc->txq);
|
||||
if (skb == NULL)
|
||||
return NULL;
|
||||
lc_debug(lc, "tx: begin new frame len: %u\n", skb->len);
|
||||
|
||||
flen = channel_sdu_size[DECT_MC_C_S];
|
||||
fill_len = roundup(skb->len + DECT_FA_CSUM_SIZE, flen) -
|
||||
(skb->len + DECT_FA_CSUM_SIZE);
|
||||
fill = skb_put(skb, fill_len);
|
||||
memset(fill, DECT_FA_FILL_PATTERN, fill_len);
|
||||
|
||||
skb_put(skb, DECT_FA_CSUM_SIZE);
|
||||
dect_fa_frame_csum(lc, skb);
|
||||
WARN_ON(!dect_fa_frame_csum_verify(lc, skb));
|
||||
|
||||
lc->tx_head = skb;
|
||||
lc->tx_len = flen;
|
||||
}
|
||||
|
||||
/* Fragment into tx_len sized units */
|
||||
if (skb->len > lc->tx_len) {
|
||||
frag = skb_copy(skb, GFP_ATOMIC);
|
||||
if (frag == NULL)
|
||||
return NULL;
|
||||
skb_trim(frag, lc->tx_len);
|
||||
skb_pull(skb, lc->tx_len);
|
||||
} else {
|
||||
frag = lc->tx_head;
|
||||
lc->tx_head = NULL;
|
||||
}
|
||||
|
||||
lc_debug(lc, "tx: %sfragment len: %u\n",
|
||||
lc->tx_head ? "" : "last ", frag->len);
|
||||
{ unsigned int i; for (i = 0; i< frag->len; i++) printk("%.2x ", frag->data[i]);
|
||||
printk("\n");}
|
||||
return frag;
|
||||
}
|
||||
|
||||
static struct sk_buff *dect_lc_reassemble(struct dect_lc *lc,
|
||||
enum dect_data_channels chan,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct dect_fa_len fl;
|
||||
u8 flen, len;
|
||||
|
||||
if (lc->rx_head == NULL) {
|
||||
dect_fa_parse_len(&fl, skb);
|
||||
len = fl.len;
|
||||
len += DECT_FA_HDR_SIZE + DECT_FA_CSUM_SIZE;
|
||||
|
||||
flen = channel_sdu_size[chan];
|
||||
lc->rx_len = roundup(len, flen);
|
||||
}
|
||||
|
||||
lc->rx_head = skb_append_frag(lc->rx_head, skb);
|
||||
skb = NULL;
|
||||
|
||||
if (lc->rx_head->len >= lc->rx_len) {
|
||||
WARN_ON(lc->rx_head->len != lc->rx_len);
|
||||
skb = lc->rx_head;
|
||||
lc->rx_head = NULL;
|
||||
|
||||
if (skb_linearize(skb) < 0)
|
||||
goto err;
|
||||
if (!dect_fa_frame_csum_verify(lc, skb))
|
||||
goto err;
|
||||
|
||||
/* Trim checksum and filling */
|
||||
dect_fa_parse_len(&fl, skb);
|
||||
skb_trim(skb, fl.len + DECT_FA_HDR_SIZE);
|
||||
lc_debug(lc, "reassembled SDU len %u\n", skb->len);
|
||||
}
|
||||
|
||||
return skb;
|
||||
|
||||
err:
|
||||
lc_debug(lc, "reassembly failed\n");
|
||||
kfree_skb(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void dect_lc_rcv(struct dect_lc *lc, enum dect_data_channels chan,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct dect_fa_hdr *fh;
|
||||
struct dect_lapc *lapc;
|
||||
struct dect_dli dli;
|
||||
enum dect_sapis sapi;
|
||||
|
||||
skb = dect_lc_reassemble(lc, chan, skb);
|
||||
if (skb == NULL)
|
||||
return;
|
||||
fh = (struct dect_fa_hdr *)skb->data;
|
||||
|
||||
dli.lln = (fh->addr & DECT_FA_ADDR_LLN_MASK) >> DECT_FA_ADDR_LLN_SHIFT;
|
||||
lc_debug(lc, "receive: LLN %u NLF %u SAPI %u\n",
|
||||
dli.lln, (fh->addr & DECT_FA_ADDR_NLF_FLAG) ? 1 : 0,
|
||||
(fh->addr & DECT_FA_ADDR_SAPI_MASK) >> DECT_FA_ADDR_SAPI_SHIFT);
|
||||
|
||||
if (lc->lapcs[dli.lln] != NULL)
|
||||
return dect_lapc_rcv(lc->lapcs[dli.lln], skb);
|
||||
|
||||
/* Link establishment: new requests are only valid while no link
|
||||
* estabishment is in progress.
|
||||
*/
|
||||
if (!(fh->addr & DECT_FA_ADDR_NLF_FLAG))
|
||||
goto err;
|
||||
if ((fh->ctrl & DECT_FA_CTRL_I_FMT_MASK) != DECT_FA_CTRL_I_FMT_ID)
|
||||
goto err;
|
||||
if (lc->elapc != NULL)
|
||||
goto err;
|
||||
|
||||
sapi = (fh->addr & DECT_FA_ADDR_SAPI_MASK) >> DECT_FA_ADDR_SAPI_SHIFT;
|
||||
if (sapi != DECT_SAPI_CO_SIGNALLING && sapi != DECT_SAPI_CL_SIGNALLING)
|
||||
goto err;
|
||||
memcpy(&dli.mci, &lc->mc->mci, sizeof(dli.mci));
|
||||
|
||||
lapc = dect_ssap_rcv_request(lc, &dli, sapi);
|
||||
if (lapc == NULL)
|
||||
goto err;
|
||||
lc->lapcs[dli.lln] = lapc;
|
||||
|
||||
return dect_lapc_rcv(lapc, skb);
|
||||
|
||||
err:
|
||||
lc_debug(lc, "packet ignored\n");
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
void dect_cplane_rcv(struct dect_mac_conn *mc, enum dect_data_channels chan,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct dect_fa_hdr *fh = (struct dect_fa_hdr *)skb->data;
|
||||
struct dect_lc *lc;
|
||||
|
||||
if (mc->lc == NULL) {
|
||||
#if 0
|
||||
if (!(fh->addr & DECT_FA_ADDR_NLF_FLAG))
|
||||
goto err;
|
||||
#endif
|
||||
lc = dect_lc_init(mc, GFP_ATOMIC);
|
||||
if (lc == NULL)
|
||||
goto err;
|
||||
mc->lc = lc;
|
||||
}
|
||||
return dect_lc_rcv(mc->lc, chan, skb);
|
||||
|
||||
err:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
struct sk_buff *dect_cplane_dtr(struct dect_mac_conn *mc, enum dect_data_channels chan)
|
||||
{
|
||||
struct dect_lc *lc;
|
||||
|
||||
lc = mc->lc;
|
||||
if (lc == NULL)
|
||||
return NULL;
|
||||
lc_debug(lc, "DTR channel %u\n", chan);
|
||||
return dect_lc_tx(lc);
|
||||
}
|
||||
|
||||
void dect_cplane_notify_state_change(struct dect_mac_conn *mc)
|
||||
{
|
||||
struct dect_lc *lc = mc->lc;
|
||||
unsigned int i;
|
||||
|
||||
if (lc == NULL)
|
||||
return;
|
||||
|
||||
lc_debug(lc, "mac conn state change: state: %u\n", mc->state);
|
||||
switch (mc->state) {
|
||||
// FIXME: this does not make sense for incoming connections
|
||||
case DECT_MAC_CONN_OPEN_PENDING:
|
||||
break;
|
||||
case DECT_MAC_CONN_OPEN:
|
||||
for (i = 0; i < ARRAY_SIZE(lc->lapcs); i++) {
|
||||
if (lc->lapcs[i] == NULL)
|
||||
continue;
|
||||
dect_lapc_establish(lc->lapcs[i]);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case DECT_MAC_CONN_CLOSED:
|
||||
for (i = 0; i < ARRAY_SIZE(lc->lapcs); i++) {
|
||||
if (lc->lapcs[i] == NULL)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* DECT DLC Layer
|
||||
*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
static void dect_dlc_fb1_enqueue(struct dect_dlc_fbx *fbx, struct sk_buff *skb)
|
||||
{
|
||||
struct dect_dlc_lux *lux = container_of(fbx, struct dect_dlc_lux, fbx);
|
||||
struct sock *sk = &lux->sk;
|
||||
|
||||
skb_queue_tail(&sk->sk_write_queue, skb);
|
||||
}
|
||||
|
||||
static struct sk_buff *dect_dlc_fb1_dequeue(struct dect_dlc_fbx *fbx)
|
||||
{
|
||||
struct dect_dlc_lux *lux = container_of(fbx, struct dect_dlc_lux, fbx);
|
||||
struct sock *sk = &lux->sk;
|
||||
struct sk_buff *skb, *clone, *head = NULL;
|
||||
int need = 40;
|
||||
|
||||
while (need > 0) {
|
||||
skb = skb_peek(&sk->sk_write_queue);
|
||||
if (skb == NULL)
|
||||
goto err;
|
||||
|
||||
if (skb->len <= need) {
|
||||
__skb_unlink(skb, &sk->sk_write_queue);
|
||||
need -= skb->len;
|
||||
} else {
|
||||
clone = skb_clone(skb, GFP_ATOMIC);
|
||||
if (clone == NULL)
|
||||
goto err;
|
||||
clone->len = need;
|
||||
skb_pull(skb, need);
|
||||
skb = clone;
|
||||
}
|
||||
head = skb_append_frag(head, skb);
|
||||
}
|
||||
return head;
|
||||
err:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct dect_dlc_fbx_ops dect_dlc_fb1_ops = {
|
||||
.enqueue = dect_dlc_fb1_enqueue,
|
||||
.dequeue = dect_dlc_fb1_dequeue,
|
||||
};
|
|
@ -0,0 +1,286 @@
|
|||
/*
|
||||
* DECT DLC LU1 SAP sockets
|
||||
*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/dect.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/dect/dect.h>
|
||||
|
||||
struct dect_lu1_sap {
|
||||
struct sock sk;
|
||||
struct dect_ulei ulei;
|
||||
struct dect_mac_conn *mc;
|
||||
struct dect_lux lux;
|
||||
};
|
||||
|
||||
static struct sk_buff *dect_lu1_dequeue(struct dect_lux *lux)
|
||||
{
|
||||
struct dect_lu1_sap *lu1 = container_of(lux, struct dect_lu1_sap, lux);
|
||||
|
||||
return skb_dequeue(&lu1->sk.sk_write_queue);
|
||||
}
|
||||
|
||||
static void dect_lu1_enqueue(struct dect_lux *lux, struct sk_buff *skb)
|
||||
{
|
||||
struct dect_lu1_sap *lu1 = container_of(lux, struct dect_lu1_sap, lux);
|
||||
|
||||
if (sock_queue_rcv_skb(&lu1->sk, skb) < 0)
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static void dect_lu1_disconnect(struct dect_lux *lux)
|
||||
{
|
||||
struct dect_lu1_sap *lu1 = container_of(lux, struct dect_lu1_sap, lux);
|
||||
struct sock *sk = &lu1->sk;
|
||||
|
||||
sk->sk_state = DECT_SK_RELEASED;
|
||||
sk->sk_err = ENETDOWN;
|
||||
if (!sock_flag(sk, SOCK_DEAD))
|
||||
sk->sk_error_report(sk);
|
||||
lu1->mc = NULL;
|
||||
}
|
||||
|
||||
static const struct dect_lux_ops dect_lu1_ops = {
|
||||
.dequeue = dect_lu1_dequeue,
|
||||
.enqueue = dect_lu1_enqueue,
|
||||
.disconnect = dect_lu1_disconnect,
|
||||
};
|
||||
|
||||
static inline struct dect_lu1_sap *dect_lu1_sap(struct sock *sk)
|
||||
{
|
||||
return (struct dect_lu1_sap *)sk;
|
||||
}
|
||||
|
||||
static int dect_parse_ulei(struct dect_ulei *ulei,
|
||||
const struct sockaddr_dect_lu *addr)
|
||||
{
|
||||
if (dect_parse_ari(&ulei->mci.ari, (u64)addr->dect_ari << 24) == 0)
|
||||
return -EINVAL;
|
||||
dect_parse_pmid(&ulei->mci.pmid, addr->dect_pmid);
|
||||
ulei->mci.lcn = addr->dect_lcn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dect_build_ulei(struct sockaddr_dect_lu *addr,
|
||||
const struct dect_ulei *ulei)
|
||||
{
|
||||
addr->dect_family = AF_DECT;
|
||||
addr->dect_pmid = dect_build_pmid(&ulei->mci.pmid);
|
||||
addr->dect_lcn = ulei->mci.lcn;
|
||||
}
|
||||
|
||||
static int dect_lu1_init(struct sock *sk)
|
||||
{
|
||||
sk->sk_state = DECT_SK_RELEASED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dect_lu1_close(struct sock *sk, long timeout)
|
||||
{
|
||||
struct dect_lu1_sap *lu1 = dect_lu1_sap(sk);
|
||||
|
||||
if (sk->sk_state == DECT_SK_ESTABLISHED) {
|
||||
lu1->mc->fbx = NULL;
|
||||
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
|
||||
}
|
||||
|
||||
__skb_queue_purge(&sk->sk_receive_queue);
|
||||
__skb_queue_purge(&sk->sk_write_queue);
|
||||
|
||||
sock_put(sk);
|
||||
}
|
||||
|
||||
static int dect_lu1_getname(struct sock *sk, struct sockaddr *uaddr,
|
||||
int *len, int peer)
|
||||
{
|
||||
struct sockaddr_dect_lu *addr = (struct sockaddr_dect_lu *)uaddr;
|
||||
struct dect_lu1_sap *lu1 = dect_lu1_sap(sk);
|
||||
|
||||
if (peer)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
dect_build_ulei(addr, &lu1->ulei);
|
||||
*len = sizeof(*addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dect_lu1_connect(struct sock *sk, struct sockaddr *uaddr, int len)
|
||||
{
|
||||
struct sockaddr_dect_lu *addr = (struct sockaddr_dect_lu *)uaddr;
|
||||
struct dect_lu1_sap *lu1 = dect_lu1_sap(sk);
|
||||
struct dect_cluster *cl;
|
||||
struct dect_ulei ulei;
|
||||
struct dect_mac_conn *mc;
|
||||
int err;
|
||||
|
||||
err = dect_parse_ulei(&ulei, addr);
|
||||
if (err < 0)
|
||||
goto err1;
|
||||
|
||||
err = -ENODEV;
|
||||
cl = dect_cluster_get_by_pari(&ulei.mci.ari);
|
||||
if (cl == NULL)
|
||||
goto err1;
|
||||
|
||||
err = -ENETDOWN;
|
||||
mc = dect_mac_conn_get_by_mci(cl, &ulei.mci);
|
||||
if (mc == NULL)
|
||||
goto err1;
|
||||
|
||||
err = -EBUSY;
|
||||
if (mc->fbx != NULL)
|
||||
goto err1;
|
||||
|
||||
memcpy(&lu1->ulei, &ulei, sizeof(lu1->ulei));
|
||||
|
||||
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
|
||||
sk->sk_state = DECT_SK_ESTABLISHED;
|
||||
|
||||
lu1->lux.fbx.ops = &dect_fbn_ops;
|
||||
lu1->lux.ops = &dect_lu1_ops;
|
||||
lu1->mc = mc;
|
||||
mc->fbx = &lu1->lux.fbx;
|
||||
printk("LU1: bound to MCEI %u\n", mc->mcei);
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dect_lu1_recvmsg(struct kiocb *iocb, struct sock *sk,
|
||||
struct msghdr *msg, size_t len,
|
||||
int noblock, int flags, int *addr_len)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
size_t copied = 0, copy;
|
||||
long timeo;
|
||||
int err;
|
||||
|
||||
if (flags & (MSG_OOB | MSG_TRUNC))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if (sk->sk_state != DECT_SK_ESTABLISHED) {
|
||||
err = -ENOTCONN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
timeo = sock_rcvtimeo(sk, noblock);
|
||||
|
||||
do {
|
||||
skb = skb_peek(&sk->sk_receive_queue);
|
||||
if (skb != NULL)
|
||||
goto copy;
|
||||
|
||||
if (!timeo) {
|
||||
err = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (signal_pending(current)) {
|
||||
err = sock_intr_errno(timeo);
|
||||
break;
|
||||
}
|
||||
|
||||
sk_wait_data(sk, &timeo);
|
||||
continue;
|
||||
|
||||
copy:
|
||||
copy = len - copied;
|
||||
if (copy > skb->len)
|
||||
copy = skb->len;
|
||||
|
||||
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copy);
|
||||
if (err < 0)
|
||||
break;
|
||||
copied += copy;
|
||||
|
||||
if (copy < skb->len) {
|
||||
__skb_pull(skb, copy);
|
||||
break;
|
||||
} else
|
||||
sk_eat_skb(sk, skb, 0);
|
||||
} while (1);
|
||||
|
||||
out:
|
||||
printk("LU1: %p: recv err %d copied %zu\n", sk, err, copied);
|
||||
release_sock(sk);
|
||||
return copied ? : err;
|
||||
}
|
||||
|
||||
static int dect_lu1_sendmsg(struct kiocb *kiocb, struct sock *sk,
|
||||
struct msghdr *msg, size_t len)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int err;
|
||||
|
||||
if (msg->msg_flags & MSG_OOB)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (sk->sk_state != DECT_SK_ESTABLISHED)
|
||||
return -ENOTCONN;
|
||||
|
||||
skb = sock_alloc_send_skb(sk, len, msg->msg_flags & MSG_DONTWAIT, &err);
|
||||
if (skb == NULL)
|
||||
goto err1;
|
||||
err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
|
||||
if (err < 0)
|
||||
goto err2;
|
||||
|
||||
skb_queue_tail(&sk->sk_write_queue, skb);
|
||||
return len;
|
||||
|
||||
err2:
|
||||
kfree_skb(skb);
|
||||
err1:
|
||||
printk("LU1: %p: send err %d wmem %u\n", sk, err, atomic_read(&sk->sk_wmem_alloc));
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct dect_proto dect_lu1_proto = {
|
||||
.type = SOCK_STREAM,
|
||||
.protocol = DECT_LU1_SAP,
|
||||
.capability = -1,
|
||||
.ops = &dect_stream_ops,
|
||||
.proto.name = "DECT_LU1_SAP",
|
||||
.proto.owner = THIS_MODULE,
|
||||
.proto.obj_size = sizeof(struct dect_lu1_sap),
|
||||
.proto.init = dect_lu1_init,
|
||||
.proto.close = dect_lu1_close,
|
||||
.proto.connect = dect_lu1_connect,
|
||||
.proto.recvmsg = dect_lu1_recvmsg,
|
||||
.proto.sendmsg = dect_lu1_sendmsg,
|
||||
.getname = dect_lu1_getname,
|
||||
};
|
||||
|
||||
int __init dect_lu1_sap_module_init(void)
|
||||
{
|
||||
return dect_proto_register(&dect_lu1_proto);
|
||||
}
|
||||
|
||||
void dect_lu1_sap_module_exit(void)
|
||||
{
|
||||
dect_proto_unregister(&dect_lu1_proto);
|
||||
}
|
||||
#if 0
|
||||
module_init(dect_lu1_init);
|
||||
module_exit(dect_lu1_exit);
|
||||
|
||||
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
|
||||
MODULE_DESCRIPTION("DECT DLC LU1 SAP sockets");
|
||||
MODULE_LICENSE("GPL");
|
||||
#endif
|
||||
|
||||
MODULE_ALIAS_NET_PF_PROTO(PF_DECT, DECT_LU1);
|
|
@ -0,0 +1,564 @@
|
|||
/*
|
||||
* DECT DLC S SAP sockets - DLC C-plane data link service access
|
||||
*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define DEBUG
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/dect.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/dect/dect.h>
|
||||
|
||||
static DEFINE_SPINLOCK(dect_ssap_lock);
|
||||
static HLIST_HEAD(dect_ssap_sockets);
|
||||
static HLIST_HEAD(dect_ssap_listeners);
|
||||
|
||||
#define DECT_LLN_ANY 255
|
||||
|
||||
struct dect_ssap {
|
||||
struct dect_csk csk;
|
||||
struct dect_dlei dlei;
|
||||
struct dect_lapc *lapc;
|
||||
};
|
||||
|
||||
static inline struct dect_ssap *dect_ssap(struct sock *sk)
|
||||
{
|
||||
return (struct dect_ssap *)sk;
|
||||
}
|
||||
|
||||
static int dect_parse_dlei(struct dect_dlei *dlei,
|
||||
const struct sockaddr_dect_ssap *addr)
|
||||
{
|
||||
if (dect_parse_ari(&dlei->mci.ari, (u64)addr->dect_ari << 24) == 0)
|
||||
return -EINVAL;
|
||||
dect_parse_pmid(&dlei->mci.pmid, addr->dect_pmid);
|
||||
dlei->mci.lcn = addr->dect_lcn;
|
||||
|
||||
dlei->lln = addr->dect_lln;
|
||||
if (dlei->lln > DECT_LLN_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
dlei->sapi = addr->dect_sapi;
|
||||
switch (dlei->sapi) {
|
||||
case DECT_SAPI_CO_SIGNALLING:
|
||||
case DECT_SAPI_CL_SIGNALLING:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dect_build_dlei(struct sockaddr_dect_ssap *addr,
|
||||
const struct dect_dlei *dlei)
|
||||
{
|
||||
addr->dect_family = AF_DECT;
|
||||
addr->dect_pmid = dect_build_pmid(&dlei->mci.pmid);
|
||||
addr->dect_ari = dect_build_ari(&dlei->mci.ari) >> 24;
|
||||
addr->dect_lcn = dlei->mci.lcn;
|
||||
addr->dect_lln = dlei->lln;
|
||||
addr->dect_sapi = dlei->sapi;
|
||||
}
|
||||
|
||||
static void dect_ssap_insert(struct sock *sk)
|
||||
{
|
||||
sk_add_node(sk, &dect_ssap_sockets);
|
||||
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
|
||||
}
|
||||
|
||||
static void dect_ssap_unlink(struct sock *sk)
|
||||
{
|
||||
if (sk_del_node_init(sk))
|
||||
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
|
||||
}
|
||||
|
||||
static int dect_ssap_init(struct sock *sk)
|
||||
{
|
||||
struct dect_ssap *ssap = dect_ssap(sk);
|
||||
|
||||
INIT_HLIST_HEAD(&ssap->csk.accept_queue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sock *dect_ssap_acceptq_dequeue(struct dect_ssap *ssap)
|
||||
{
|
||||
struct sock *sk;
|
||||
|
||||
if (hlist_empty(&ssap->csk.accept_queue))
|
||||
return NULL;
|
||||
sk = hlist_entry(ssap->csk.accept_queue.first, struct sock, sk_bind_node);
|
||||
__sk_del_bind_node(sk);
|
||||
sk_node_init(&sk->sk_bind_node);
|
||||
sk_acceptq_removed(&ssap->csk.sk);
|
||||
return sk;
|
||||
}
|
||||
|
||||
static void dect_ssap_close(struct sock *sk, long timeout)
|
||||
{
|
||||
struct dect_ssap *ssap = dect_ssap(sk);
|
||||
struct sock *req;
|
||||
|
||||
printk("close sock %p\n", sk);
|
||||
dect_ssap_unlink(sk);
|
||||
|
||||
if (ssap->lapc != NULL) {
|
||||
sock_put(ssap->lapc->sk);
|
||||
dect_lapc_release(ssap->lapc);
|
||||
}
|
||||
|
||||
if (!hlist_unhashed(&sk->sk_bind_node))
|
||||
__sk_del_bind_node(sk);
|
||||
|
||||
while ((req = dect_ssap_acceptq_dequeue(ssap)) != NULL) {
|
||||
dect_ssap_unlink(req);
|
||||
dect_lapc_release(dect_ssap(req)->lapc);
|
||||
sock_put(req);
|
||||
}
|
||||
|
||||
sk_common_release(sk);
|
||||
}
|
||||
|
||||
static int dect_ssap_bind_conflict(const struct dect_dlei *dlei)
|
||||
{
|
||||
struct dect_ssap *ssap;
|
||||
struct hlist_node *n;
|
||||
struct sock *sk;
|
||||
|
||||
// FIXME: wildcards
|
||||
sk_for_each(sk, n, &dect_ssap_sockets) {
|
||||
ssap = dect_ssap(sk);
|
||||
if (!dect_pmid_cmp(&ssap->dlei.mci.pmid, &dlei->mci.pmid) &&
|
||||
ssap->dlei.lln == dlei->lln)
|
||||
return -EADDRINUSE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dect_ssap_bind(struct sock *sk, struct sockaddr *uaddr, int len)
|
||||
{
|
||||
struct sockaddr_dect_ssap *addr = (struct sockaddr_dect_ssap *)uaddr;
|
||||
struct dect_ssap *ssap = dect_ssap(sk);
|
||||
struct dect_dlei dlei;
|
||||
int err;
|
||||
|
||||
if (len < sizeof(*addr) || addr->dect_family != AF_DECT)
|
||||
return -EINVAL;
|
||||
|
||||
err = dect_parse_dlei(&dlei, addr);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
lock_sock(sk);
|
||||
spin_lock_bh(&dect_ssap_lock);
|
||||
|
||||
err = dect_ssap_bind_conflict(&dlei);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
memcpy(&ssap->dlei, &dlei, sizeof(ssap->dlei));
|
||||
dect_ssap_insert(sk);
|
||||
out:
|
||||
spin_unlock_bh(&dect_ssap_lock);
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct dect_ssap *dect_ssap_lookup_listener(const struct dect_dli *dli,
|
||||
enum dect_sapis sapi)
|
||||
{
|
||||
struct dect_ssap *ssap;
|
||||
struct hlist_node *n;
|
||||
struct sock *sk;
|
||||
|
||||
pr_debug("lookup listener: lln %u sapi %u\n", dli->lln, sapi);
|
||||
sk_for_each_bound(sk, n, &dect_ssap_listeners) {
|
||||
ssap = dect_ssap(sk);
|
||||
#if 0
|
||||
if (!dect_ari_cmp(&ssap->dlei.mci.ari, &dli->mci.ari))
|
||||
continue;
|
||||
if (!dect_pmid_cmp(&ssap->dlei.mci.pmid, &dli->mci.pmid))
|
||||
continue;
|
||||
#endif
|
||||
pr_debug("ssap: lln %u sapi %u\n", ssap->dlei.lln, ssap->dlei.sapi);
|
||||
if (ssap->dlei.lln != DECT_LLN_ANY &&
|
||||
ssap->dlei.lln != dli->lln)
|
||||
continue;
|
||||
if (ssap->dlei.sapi != sapi)
|
||||
continue;
|
||||
return ssap;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dect_lapc *dect_ssap_rcv_request(struct dect_lc *lc,
|
||||
const struct dect_dli *dli,
|
||||
enum dect_sapis sapi)
|
||||
{
|
||||
struct dect_ssap *ssap, *newssap;
|
||||
struct sock *sk, *newsk;
|
||||
struct dect_lapc *lapc = NULL;
|
||||
|
||||
spin_lock(&dect_ssap_lock);
|
||||
ssap = dect_ssap_lookup_listener(dli, sapi);
|
||||
if (ssap == NULL)
|
||||
goto out;
|
||||
|
||||
sk = &ssap->csk.sk;
|
||||
if (sk_acceptq_is_full(sk))
|
||||
goto out;
|
||||
|
||||
newsk = sk_alloc(&init_net, PF_DECT, GFP_ATOMIC, sk->sk_prot);
|
||||
if (newsk == NULL)
|
||||
goto out;
|
||||
|
||||
lapc = dect_lapc_init(dli, sapi, lc, GFP_ATOMIC);
|
||||
if (lapc == NULL)
|
||||
goto err1;
|
||||
sock_hold(newsk);
|
||||
lapc->sk = newsk;
|
||||
|
||||
sock_init_data(NULL, newsk);
|
||||
newsk->sk_protocol = sk->sk_protocol;
|
||||
newsk->sk_destruct = sk->sk_destruct;
|
||||
|
||||
newssap = dect_ssap(newsk);
|
||||
memcpy(&newssap->dlei.mci, &dli->mci, sizeof(newssap->dlei.mci));
|
||||
newssap->dlei.lln = dli->lln;
|
||||
newssap->dlei.sapi = sapi;
|
||||
newssap->lapc = lapc;
|
||||
|
||||
newsk->sk_state = DECT_SK_ESTABLISHED;
|
||||
dect_ssap_insert(newsk);
|
||||
sk_add_bind_node(newsk, &ssap->csk.accept_queue);
|
||||
sk_acceptq_added(sk);
|
||||
|
||||
sk->sk_state_change(sk);
|
||||
sk->sk_data_ready(sk, 0);
|
||||
out:
|
||||
spin_unlock(&dect_ssap_lock);
|
||||
return lapc;
|
||||
|
||||
err1:
|
||||
sk_free(sk);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void dect_ssap_hash(struct sock *sk)
|
||||
{
|
||||
sk->sk_state = DECT_SK_LISTEN;
|
||||
|
||||
spin_lock_bh(&dect_ssap_lock);
|
||||
sk_add_bind_node(sk, &dect_ssap_listeners);
|
||||
spin_unlock_bh(&dect_ssap_lock);
|
||||
}
|
||||
|
||||
static void dect_ssap_unhash(struct sock *sk)
|
||||
{
|
||||
if (sk_hashed(sk)) {
|
||||
spin_lock_bh(&dect_ssap_lock);
|
||||
__sk_del_bind_node(sk);
|
||||
spin_unlock_bh(&dect_ssap_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static int dect_ssap_wait_req(struct sock *sk, int noblock)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
struct dect_ssap *ssap = dect_ssap(sk);
|
||||
long timeo = sock_rcvtimeo(sk, noblock);
|
||||
|
||||
for (;;) {
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
if (sk->sk_state != DECT_SK_LISTEN)
|
||||
return -EINVAL;
|
||||
if (!hlist_empty(&ssap->csk.accept_queue))
|
||||
break;
|
||||
if (!timeo)
|
||||
return -EWOULDBLOCK;
|
||||
if (signal_pending(tsk))
|
||||
return sock_intr_errno(timeo);
|
||||
|
||||
prepare_to_wait_exclusive(&sk->sk_socket->wait, &wait,
|
||||
TASK_INTERRUPTIBLE);
|
||||
release_sock(sk);
|
||||
timeo = schedule_timeout(timeo);
|
||||
lock_sock(sk);
|
||||
finish_wait(&sk->sk_socket->wait, &wait);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sock *dect_ssap_accept(struct sock *sk, int flags, int *errp)
|
||||
{
|
||||
struct dect_ssap *ssap = dect_ssap(sk);
|
||||
struct sock *newsk;
|
||||
int err;
|
||||
|
||||
lock_sock(sk);
|
||||
err = dect_ssap_wait_req(sk, flags & O_NONBLOCK);
|
||||
if (err < 0)
|
||||
goto err;
|
||||
|
||||
newsk = dect_ssap_acceptq_dequeue(ssap);
|
||||
release_sock(sk);
|
||||
|
||||
*errp = 0;
|
||||
return newsk;
|
||||
|
||||
err:
|
||||
release_sock(sk);
|
||||
*errp = err;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int dect_ssap_connect(struct sock *sk, struct sockaddr *uaddr, int len)
|
||||
{
|
||||
struct sockaddr_dect_ssap *addr = (struct sockaddr_dect_ssap *)uaddr;
|
||||
struct dect_ssap *ssap = dect_ssap(sk);
|
||||
struct dect_cluster *cl;
|
||||
struct dect_dlei dlei;
|
||||
struct dect_dli dli;
|
||||
struct dect_lapc *lapc;
|
||||
struct dect_lc *lc;
|
||||
struct dect_mac_conn *mc;
|
||||
bool new_mc = false, new_lc = false;
|
||||
int err;
|
||||
|
||||
if (len < sizeof(*addr) || addr->dect_family != AF_DECT)
|
||||
return -EINVAL;
|
||||
|
||||
err = dect_parse_dlei(&dlei, addr);
|
||||
if (err < 0)
|
||||
goto err1;
|
||||
|
||||
err = -ENODEV;
|
||||
cl = dect_cluster_get_by_pari(&dlei.mci.ari);
|
||||
if (cl == NULL)
|
||||
goto err1;
|
||||
|
||||
/* The assignable class B LLNs may only be used for connections
|
||||
* originating from a PT. The unassigned LLN may be used by an FT
|
||||
* to request class B operation. Class A and U may be used by both.
|
||||
*/
|
||||
err = -EINVAL;
|
||||
switch (dlei.lln) {
|
||||
case DECT_LLN_ASSIGNABLE_MIN ... DECT_LLN_ASSIGNABLE_MAX:
|
||||
if (cl->mode != DECT_MODE_PP)
|
||||
goto err1;
|
||||
break;
|
||||
case DECT_LLN_UNASSIGNED:
|
||||
if (cl->mode != DECT_MODE_FP)
|
||||
goto err1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Lookup MAC connection and initiate new one if necessary */
|
||||
err = -ENOMEM;
|
||||
mc = dect_mac_conn_get_by_mci(cl, &dlei.mci);
|
||||
if (mc == NULL) {
|
||||
mc = dect_mac_conn_init(cl, &dlei.mci, NULL);
|
||||
if (mc == NULL)
|
||||
goto err1;
|
||||
new_mc = true;
|
||||
lc = NULL;
|
||||
} else
|
||||
lc = mc->lc;
|
||||
|
||||
/* Get Lc entity and verify LLN is available */
|
||||
if (lc == NULL) {
|
||||
lc = dect_lc_init(mc, GFP_KERNEL);
|
||||
if (lc == NULL)
|
||||
goto err2;
|
||||
mc->lc = lc;
|
||||
new_lc = true;
|
||||
} else {
|
||||
err = -EADDRINUSE;
|
||||
if (lc->lapcs[dlei.lln] != NULL)
|
||||
goto err2;
|
||||
}
|
||||
|
||||
memcpy(&dli.mci, &dlei.mci, sizeof(dli.mci));
|
||||
dli.lln = dlei.lln;
|
||||
|
||||
lapc = dect_lapc_init(&dli, dlei.sapi, lc, GFP_KERNEL);
|
||||
if (lapc == NULL)
|
||||
goto err3;
|
||||
ssap->lapc = lapc;
|
||||
sock_hold(sk);
|
||||
lapc->sk = sk;
|
||||
|
||||
lc->lapcs[dlei.lln] = lapc;
|
||||
|
||||
if (new_mc)
|
||||
err = dect_dlc_mac_conn_establish(mc);
|
||||
else
|
||||
dect_lapc_establish(lapc);
|
||||
|
||||
sk->sk_state = DECT_SK_ESTABLISH_PENDING;
|
||||
return 0;
|
||||
err3:
|
||||
err2:
|
||||
if (new_mc)
|
||||
dect_dlc_mac_conn_release(mc);
|
||||
err1:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dect_ssap_getname(struct sock *sk, struct sockaddr *uaddr, int *len,
|
||||
int peer)
|
||||
{
|
||||
struct sockaddr_dect_ssap *addr = (struct sockaddr_dect_ssap *)uaddr;
|
||||
struct dect_ssap *ssap = dect_ssap(sk);
|
||||
|
||||
#if 0
|
||||
if (peer)
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
dect_build_dlei(addr, &ssap->dlei);
|
||||
*len = sizeof(*addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dect_ssap_recvmsg(struct kiocb *iocb, struct sock *sk,
|
||||
struct msghdr *msg, size_t len,
|
||||
int noblock, int flags, int *addr_len)
|
||||
{
|
||||
struct sockaddr_dect *addr;
|
||||
struct sk_buff *skb;
|
||||
size_t copied = 0;
|
||||
int err;
|
||||
|
||||
if (flags & MSG_OOB)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (sk->sk_type == SOCK_SEQPACKET) {
|
||||
lock_sock(sk);
|
||||
if (sk->sk_state != DECT_SK_ESTABLISHED) {
|
||||
release_sock(sk);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
release_sock(sk);
|
||||
}
|
||||
|
||||
skb = skb_recv_datagram(sk, flags, noblock, &err);
|
||||
if (skb == NULL)
|
||||
goto out;
|
||||
|
||||
copied = skb->len;
|
||||
if (len < copied) {
|
||||
msg->msg_flags |= MSG_TRUNC;
|
||||
copied = len;
|
||||
}
|
||||
|
||||
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
|
||||
if (msg->msg_name != NULL) {
|
||||
addr = (struct sockaddr_dect *)msg->msg_name;
|
||||
addr->dect_family = AF_DECT;
|
||||
addr->dect_index = DECT_SK_CB(skb)->index;
|
||||
msg->msg_namelen = sizeof(*addr);
|
||||
}
|
||||
|
||||
sock_recv_timestamp(msg, sk, skb);
|
||||
|
||||
if (flags & MSG_TRUNC)
|
||||
copied = skb->len;
|
||||
out_free:
|
||||
skb_free_datagram(sk, skb);
|
||||
out:
|
||||
return err ? : copied;
|
||||
}
|
||||
|
||||
static int dect_ssap_sendmsg(struct kiocb *kiocb, struct sock *sk,
|
||||
struct msghdr *msg, size_t len)
|
||||
{
|
||||
struct dect_ssap *ssap = dect_ssap(sk);
|
||||
struct sk_buff *skb;
|
||||
long timeo;
|
||||
int err;
|
||||
|
||||
if (msg->msg_flags & MSG_OOB)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (len > DECT_FA_I_MAX)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (sk->sk_type == SOCK_SEQPACKET) {
|
||||
lock_sock(sk);
|
||||
if (sk->sk_state != DECT_SK_ESTABLISHED) {
|
||||
timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
|
||||
err = sk_stream_wait_connect(sk, &timeo);
|
||||
if (err < 0) {
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
release_sock(sk);
|
||||
}
|
||||
|
||||
skb = sock_alloc_send_skb(sk, len + 16, msg->msg_flags & MSG_DONTWAIT, &err);
|
||||
if (skb == NULL)
|
||||
goto err1;
|
||||
skb_reset_mac_header(skb);
|
||||
skb_reserve(skb, 16);
|
||||
err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
|
||||
if (err < 0)
|
||||
goto err2;
|
||||
|
||||
skb_queue_tail(&sk->sk_write_queue, skb);
|
||||
dect_lapc_transmit(ssap->lapc);
|
||||
return len;
|
||||
|
||||
err2:
|
||||
kfree_skb(skb);
|
||||
err1:
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct dect_proto dect_ssap_proto __read_mostly = {
|
||||
.type = SOCK_SEQPACKET,
|
||||
.protocol = DECT_S_SAP,
|
||||
.capability = CAP_NET_RAW,
|
||||
.ops = &dect_stream_ops,
|
||||
.proto.name = "DECT_S_SAP",
|
||||
.proto.owner = THIS_MODULE,
|
||||
.proto.obj_size = sizeof(struct dect_ssap),
|
||||
.proto.init = dect_ssap_init,
|
||||
.proto.close = dect_ssap_close,
|
||||
.proto.bind = dect_ssap_bind,
|
||||
.proto.hash = dect_ssap_hash,
|
||||
.proto.unhash = dect_ssap_unhash,
|
||||
.proto.accept = dect_ssap_accept,
|
||||
.proto.connect = dect_ssap_connect,
|
||||
.proto.recvmsg = dect_ssap_recvmsg,
|
||||
.proto.sendmsg = dect_ssap_sendmsg,
|
||||
.getname = dect_ssap_getname,
|
||||
};
|
||||
|
||||
int __init dect_ssap_module_init(void)
|
||||
{
|
||||
BUILD_BUG_ON(sizeof(struct sockaddr_dect_ssap) >
|
||||
sizeof(struct sockaddr));
|
||||
return dect_proto_register(&dect_ssap_proto);
|
||||
}
|
||||
|
||||
void dect_ssap_module_exit(void)
|
||||
{
|
||||
dect_proto_unregister(&dect_ssap_proto);
|
||||
}
|
||||
|
||||
MODULE_ALIAS_NET_PF_PROTO(PF_DECT, DECT_S_SAP);
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* DECT DLC U-plane
|
||||
*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define DEBUG
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/dect.h>
|
||||
#include <net/dect/dect.h>
|
||||
|
||||
static struct sk_buff *dect_fbn_dequeue(struct dect_fbx *fbx)
|
||||
{
|
||||
struct dect_lux *lux = container_of(fbx, struct dect_lux, fbx);
|
||||
|
||||
return lux->ops->dequeue(lux);
|
||||
}
|
||||
|
||||
static void dect_fbn_enqueue(struct dect_fbx *fbx, struct sk_buff *skb)
|
||||
{
|
||||
struct dect_lux *lux = container_of(fbx, struct dect_lux, fbx);
|
||||
|
||||
lux->ops->enqueue(lux, skb);
|
||||
}
|
||||
|
||||
const struct dect_fbx_ops dect_fbn_ops = {
|
||||
.dequeue = dect_fbn_dequeue,
|
||||
.enqueue = dect_fbn_enqueue,
|
||||
};
|
||||
|
||||
struct sk_buff *dect_uplane_dtr(struct dect_mac_conn *mc, enum dect_data_channels chan)
|
||||
{
|
||||
struct dect_fbx *fbx;
|
||||
|
||||
fbx = mc->fbx;
|
||||
if (fbx == NULL)
|
||||
return NULL;
|
||||
return fbx->ops->dequeue(fbx);
|
||||
}
|
||||
|
||||
void dect_uplane_rcv(struct dect_mac_conn *mc, enum dect_data_channels chan,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct dect_fbx *fbx;
|
||||
|
||||
fbx = mc->fbx;
|
||||
if (fbx == NULL)
|
||||
return;
|
||||
fbx->ops->enqueue(fbx, skb);
|
||||
}
|
||||
|
||||
void dect_uplane_notify_state_change(struct dect_mac_conn *mc)
|
||||
{
|
||||
struct dect_lux *lux;
|
||||
struct dect_fbx *fbx;
|
||||
|
||||
fbx = mc->fbx;
|
||||
if (fbx == NULL)
|
||||
return;
|
||||
lux = container_of(fbx, struct dect_lux, fbx);
|
||||
|
||||
switch (mc->state) {
|
||||
case DECT_MAC_CONN_OPEN_PENDING:
|
||||
break;
|
||||
case DECT_MAC_CONN_OPEN:
|
||||
break;
|
||||
case DECT_MAC_CONN_CLOSED:
|
||||
return lux->ops->disconnect(lux);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/dect.h>
|
||||
#include <net/dect/dect.h>
|
||||
|
||||
bool dect_ari_masked_cmp(const struct dect_ari *a1, const struct dect_ari *a2,
|
||||
const struct dect_ari *m)
|
||||
{
|
||||
/* An empty class mask implies a wildcard for everything */
|
||||
if (!m->arc)
|
||||
return false;
|
||||
if (a1->arc != a2->arc)
|
||||
return true;
|
||||
|
||||
if ((a1->fpn ^ a2->fpn) & m->fpn)
|
||||
return true;
|
||||
|
||||
switch (a1->arc) {
|
||||
case DECT_ARC_A:
|
||||
return ((a1->emc ^ a2->emc) & m->emc);
|
||||
case DECT_ARC_B:
|
||||
return (((a1->eic ^ a2->eic) & m->eic) |
|
||||
((a1->fps ^ a2->fps) & m->fps));
|
||||
case DECT_ARC_C:
|
||||
return (((a1->poc ^ a2->poc) & m->poc) |
|
||||
((a1->fps ^ a2->fps) & m->fps));
|
||||
case DECT_ARC_D:
|
||||
return ((a1->gop ^ a2->gop) & m->gop);
|
||||
case DECT_ARC_E:
|
||||
return ((a1->fil ^ a2->fil) & m->fil);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool dect_ari_cmp(const struct dect_ari *a1, const struct dect_ari *a2)
|
||||
{
|
||||
static const struct dect_ari mask = {
|
||||
.arc = ~0,
|
||||
.fpn = ~0,
|
||||
.fps = ~0,
|
||||
{ ~0 }
|
||||
};
|
||||
return dect_ari_masked_cmp(a1, a2, &mask);
|
||||
}
|
||||
|
||||
u8 dect_parse_ari(struct dect_ari *ari, u64 a)
|
||||
{
|
||||
ari->arc = (a & DECT_ARI_ARC_MASK) >> DECT_ARI_ARC_SHIFT;
|
||||
switch (ari->arc) {
|
||||
case DECT_ARC_A:
|
||||
ari->emc = (a & DECT_ARI_A_EMC_MASK) >> DECT_ARI_A_EMC_SHIFT;
|
||||
ari->fpn = (a & DECT_ARI_A_FPN_MASK) >> DECT_ARI_A_FPN_SHIFT;
|
||||
return DECT_ARC_A_LEN;
|
||||
case DECT_ARC_B:
|
||||
ari->eic = (a & DECT_ARI_B_EIC_MASK) >> DECT_ARI_B_EIC_SHIFT;
|
||||
ari->fpn = (a & DECT_ARI_B_FPN_MASK) >> DECT_ARI_B_FPN_SHIFT;
|
||||
ari->fps = (a & DECT_ARI_B_FPS_MASK) >> DECT_ARI_B_FPS_SHIFT;
|
||||
return DECT_ARC_B_LEN;
|
||||
case DECT_ARC_C:
|
||||
ari->poc = (a & DECT_ARI_C_POC_MASK) >> DECT_ARI_C_POC_SHIFT;
|
||||
ari->fpn = (a & DECT_ARI_C_FPN_MASK) >> DECT_ARI_C_FPN_SHIFT;
|
||||
ari->fps = (a & DECT_ARI_C_FPS_MASK) >> DECT_ARI_C_FPS_SHIFT;
|
||||
return DECT_ARC_C_LEN;
|
||||
case DECT_ARC_D:
|
||||
ari->gop = (a & DECT_ARI_D_GOP_MASK) >> DECT_ARI_D_GOP_SHIFT;
|
||||
ari->fpn = (a & DECT_ARI_D_FPN_MASK) >> DECT_ARI_D_FPN_SHIFT;
|
||||
return DECT_ARC_D_LEN;
|
||||
case DECT_ARC_E:
|
||||
ari->fil = (a & DECT_ARI_E_FIL_MASK) >> DECT_ARI_E_FIL_SHIFT;
|
||||
ari->fpn = (a & DECT_ARI_E_FPN_MASK) >> DECT_ARI_E_FPN_SHIFT;
|
||||
return DECT_ARC_E_LEN;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dect_parse_ari);
|
||||
|
||||
u64 dect_build_ari(const struct dect_ari *ari)
|
||||
{
|
||||
u64 a = 0;
|
||||
|
||||
a |= (u64)ari->arc << DECT_ARI_ARC_SHIFT;
|
||||
switch (ari->arc) {
|
||||
case DECT_ARC_A:
|
||||
a |= (u64)ari->emc << DECT_ARI_A_EMC_SHIFT;
|
||||
a |= (u64)ari->fpn << DECT_ARI_A_FPN_SHIFT;
|
||||
break;
|
||||
case DECT_ARC_B:
|
||||
a |= (u64)ari->eic << DECT_ARI_B_EIC_SHIFT;
|
||||
a |= (u64)ari->fpn << DECT_ARI_B_FPN_SHIFT;
|
||||
a |= (u64)ari->fps << DECT_ARI_B_FPS_SHIFT;
|
||||
break;
|
||||
case DECT_ARC_C:
|
||||
a |= (u64)ari->poc << DECT_ARI_C_POC_SHIFT;
|
||||
a |= (u64)ari->fpn << DECT_ARI_C_FPN_SHIFT;
|
||||
a |= (u64)ari->fps << DECT_ARI_C_FPS_SHIFT;
|
||||
break;
|
||||
case DECT_ARC_D:
|
||||
a |= (u64)ari->gop << DECT_ARI_D_GOP_SHIFT;
|
||||
a |= (u64)ari->fpn << DECT_ARI_D_FPN_SHIFT;
|
||||
break;
|
||||
case DECT_ARC_E:
|
||||
a |= (u64)ari->fil << DECT_ARI_E_FIL_SHIFT;
|
||||
a |= (u64)ari->fpn << DECT_ARI_E_FPN_SHIFT;
|
||||
break;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
u64 dect_build_rfpi(const struct dect_idi *idi)
|
||||
{
|
||||
u64 t = 0;
|
||||
|
||||
t |= idi->e ? DECT_RFPI_E_FLAG : 0;
|
||||
t |= dect_build_ari(&idi->pari) >> DECT_RFPI_ARI_SHIFT;
|
||||
t |= idi->rpn << DECT_RFPI_RPN_SHIFT;
|
||||
return t;
|
||||
}
|
||||
|
||||
bool dect_rfpi_cmp(const struct dect_idi *i1, const struct dect_idi *i2)
|
||||
{
|
||||
return dect_ari_cmp(&i1->pari, &i2->pari) ||
|
||||
i1->rpn != i2->rpn ||
|
||||
i1->e != i2->e;
|
||||
}
|
||||
|
||||
u16 dect_build_fmid(const struct dect_idi *idi)
|
||||
{
|
||||
u64 rfpi;
|
||||
|
||||
rfpi = dect_build_rfpi(idi);
|
||||
rfpi >>= (sizeof(rfpi) - DECT_NT_ID_RFPI_LEN) * BITS_PER_BYTE;
|
||||
return rfpi & DECT_FMID_MASK;
|
||||
}
|
||||
|
||||
/*
|
||||
* PMID (Portable MAC Identity)
|
||||
*/
|
||||
|
||||
void dect_parse_pmid(struct dect_pmid *pmid, u32 p)
|
||||
{
|
||||
if ((p & DECT_PMID_DEFAULT_ID_MASK) == DECT_PMID_DEFAULT_ID) {
|
||||
pmid->type = DECT_PMID_DEFAULT;
|
||||
pmid->num = p & DECT_PMID_DEFAULT_NUM_MASK;
|
||||
} else if ((p & DECT_PMID_EMERGENCY_ID_MASK) == DECT_PMID_EMERGENCY_ID) {
|
||||
pmid->type = DECT_PMID_EMERGENCY;
|
||||
pmid->tpui = p & DECT_PMID_EMERGENCY_TPUI_MASK;
|
||||
} else {
|
||||
pmid->type = DECT_PMID_ASSIGNED;
|
||||
pmid->tpui = p & DECT_PMID_ASSIGNED_TPUI_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
u32 dect_build_pmid(const struct dect_pmid *pmid)
|
||||
{
|
||||
u32 p = 0;
|
||||
|
||||
switch (pmid->type) {
|
||||
case DECT_PMID_DEFAULT:
|
||||
p |= DECT_PMID_DEFAULT_ID;
|
||||
p |= pmid->tpui;
|
||||
break;
|
||||
case DECT_PMID_EMERGENCY:
|
||||
p |= DECT_PMID_EMERGENCY_ID;
|
||||
p |= pmid->tpui;
|
||||
break;
|
||||
case DECT_PMID_ASSIGNED:
|
||||
p |= pmid->tpui;
|
||||
break;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
bool dect_pmid_cmp(const struct dect_pmid *p1, const struct dect_pmid *p2)
|
||||
{
|
||||
return memcmp(p1, p2, sizeof(*p1));
|
||||
}
|
||||
|
||||
/**
|
||||
* dect_parse_mci - Extract the MCI elements from a packed MCI in a
|
||||
* struct sockaddr_dect_lu
|
||||
*
|
||||
* The packed MCI is build from ARI + PMID + LCN
|
||||
*/
|
||||
int dect_parse_mci(struct dect_mci *mci, u64 m)
|
||||
{
|
||||
u32 p;
|
||||
u8 len;
|
||||
|
||||
len = dect_parse_ari(&mci->ari, m);
|
||||
|
||||
len += DECT_PMID_SIZE;
|
||||
p = (m >> (sizeof(m) * BITS_PER_BYTE - len)) & DECT_PMID_MASK;
|
||||
dect_parse_pmid(&mci->pmid, p);
|
||||
|
||||
len += DECT_ECN_SIZE;
|
||||
mci->lcn = (m >> (sizeof(m) * BITS_PER_BYTE - len)) & DECT_LCN_MASK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 dect_build_mci(const struct dect_mci *mci)
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,482 @@
|
|||
/*
|
||||
* DECT MAC Cluster Control Functions
|
||||
*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define DEBUG
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/dect.h>
|
||||
#include <net/dect/dect.h>
|
||||
#include <net/dect/mac_ccf.h>
|
||||
#include <net/dect/mac_csf.h>
|
||||
#include <net/dect/ccp.h>
|
||||
|
||||
static struct dect_cell_handle *
|
||||
dect_cluster_get_cell_by_rpn(struct dect_cluster *cl, u8 rpn)
|
||||
{
|
||||
struct dect_cell_handle *ch;
|
||||
|
||||
list_for_each_entry(ch, &cl->cells, list) {
|
||||
if (ch->rpn == rpn)
|
||||
return ch;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void dect_scan_report(const struct dect_cluster_handle *clh,
|
||||
const struct dect_scan_result *res)
|
||||
{
|
||||
struct dect_cluster *cl = container_of(clh, struct dect_cluster, handle);
|
||||
|
||||
dect_llme_scan_result_notify(cl, res);
|
||||
}
|
||||
|
||||
static void dect_mac_info_indicate(const struct dect_cluster_handle *clh,
|
||||
const struct dect_idi *idi,
|
||||
const struct dect_si *si)
|
||||
{
|
||||
struct dect_cluster *cl = container_of(clh, struct dect_cluster, handle);
|
||||
|
||||
printk("cl %p: mac info indicate\n", cl);
|
||||
memcpy(&cl->si, si, sizeof(cl->si));
|
||||
}
|
||||
|
||||
/*
|
||||
* Broadcast message control
|
||||
*/
|
||||
|
||||
/**
|
||||
* dect_bmc_mac_page_request - queue one segment of B_S channel data
|
||||
*
|
||||
* @cl: DECT cluster
|
||||
* @skb: SDU
|
||||
* @expedited: fast/normal page indication
|
||||
*/
|
||||
void dect_bmc_mac_page_request(struct dect_cluster *cl, struct sk_buff *skb,
|
||||
bool expedited)
|
||||
{
|
||||
const struct dect_cell_handle *ch, *last = NULL;
|
||||
struct sk_buff *clone;
|
||||
|
||||
BUG_ON(cl->mode != DECT_MODE_FP);
|
||||
|
||||
DECT_BMC_CB(skb)->fast = expedited;
|
||||
list_for_each_entry(ch, &cl->cells, list) {
|
||||
if (last != NULL) {
|
||||
clone = skb_clone(skb, GFP_ATOMIC);
|
||||
if (clone != NULL)
|
||||
ch->ops->page_request(ch, clone);
|
||||
}
|
||||
last = ch;
|
||||
}
|
||||
if (last != NULL)
|
||||
last->ops->page_request(last, skb);
|
||||
}
|
||||
|
||||
static void dect_bmc_page_indicate(const struct dect_cluster_handle *clh,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct dect_cluster *cl = container_of(clh, struct dect_cluster, handle);
|
||||
|
||||
return dect_dlc_mac_page_indicate(cl, skb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Multi-Bearer Control
|
||||
*/
|
||||
|
||||
#define mbc_debug(mbc, fmt, args...) \
|
||||
pr_debug("MBC (MCEI %u): " fmt, (mbc)->id.mcei, ## args);
|
||||
|
||||
static struct dect_mbc *dect_mbc_get_by_mcei(struct dect_cluster *cl, u32 mcei)
|
||||
{
|
||||
struct dect_mbc *mbc;
|
||||
|
||||
list_for_each_entry(mbc, &cl->mbcs, list) {
|
||||
if (mbc->id.mcei == mcei)
|
||||
return mbc;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u32 dect_mbc_alloc_mcei(struct dect_cluster *cl)
|
||||
{
|
||||
u32 mcei;
|
||||
|
||||
while (1) {
|
||||
mcei = ++cl->mcei_rover;
|
||||
if (mcei == 0)
|
||||
continue;
|
||||
if (dect_mbc_get_by_mcei(cl, mcei))
|
||||
continue;
|
||||
return mcei;
|
||||
}
|
||||
}
|
||||
|
||||
static void dect_mbc_timeout(unsigned long data)
|
||||
{
|
||||
struct dect_mbc *mbc = (struct dect_mbc *)data;
|
||||
|
||||
mbc_debug(mbc, "timeout");
|
||||
}
|
||||
|
||||
static void dect_mbc_release(struct dect_mbc *mbc)
|
||||
{
|
||||
del_timer_sync(&mbc->timer);
|
||||
list_del(&mbc->list);
|
||||
#if 0
|
||||
if (ch != NULL)
|
||||
ch->ops->tbc_release(ch, &mbc->id);
|
||||
#endif
|
||||
kfree(mbc);
|
||||
}
|
||||
|
||||
static struct dect_mbc *dect_mbc_init(struct dect_cluster *cl,
|
||||
const struct dect_mbc_id *id)
|
||||
{
|
||||
struct dect_mbc *mbc;
|
||||
|
||||
mbc = kzalloc(sizeof(*mbc), GFP_ATOMIC);
|
||||
if (mbc == NULL)
|
||||
return NULL;
|
||||
memcpy(&mbc->id, id, sizeof(mbc->id));
|
||||
mbc->state = DECT_MBC_NONE;
|
||||
|
||||
setup_timer(&mbc->timer, dect_mbc_timeout, (unsigned long)mbc);
|
||||
skb_queue_head_init(&mbc->c_tx_queue);
|
||||
skb_queue_head_init(&mbc->i_tx_queue);
|
||||
skb_queue_head_init(&mbc->gf_tx_queue);
|
||||
mbc->cs_rx_seq = 1;
|
||||
mbc->cs_tx_seq = 1;
|
||||
list_add_tail(&mbc->list, &cl->mbcs);
|
||||
return mbc;
|
||||
}
|
||||
|
||||
static int dect_mbc_setup_tbc(struct dect_mbc *mbc)
|
||||
{
|
||||
const struct dect_cell_handle *ch = mbc->ch;
|
||||
struct dect_channel_desc chd;
|
||||
int err;
|
||||
|
||||
memset(&chd, 0, sizeof(chd));
|
||||
chd.pkt = DECT_PACKET_P32;
|
||||
chd.b_fmt = DECT_B_UNPROTECTED;
|
||||
|
||||
err = ch->ops->tbc_initiate(ch, &mbc->id, &chd);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mbc->setup_cnt++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dect_mbc_con_request - request a new MAC connection
|
||||
*
|
||||
* @cl: DECT cluster
|
||||
* @id: MBC identifier
|
||||
*/
|
||||
int dect_mbc_con_request(struct dect_cluster *cl, const struct dect_mbc_id *id)
|
||||
{
|
||||
struct dect_cell_handle *ch;
|
||||
struct dect_mbc *mbc;
|
||||
int err;
|
||||
|
||||
err = -EHOSTUNREACH;
|
||||
ch = dect_cluster_get_cell_by_rpn(cl, 0);
|
||||
if (ch == NULL)
|
||||
goto err1;
|
||||
|
||||
err = -ENOMEM;
|
||||
mbc = dect_mbc_init(cl, id);
|
||||
if (mbc == NULL)
|
||||
goto err1;
|
||||
mbc->state = DECT_MBC_INITIATED;
|
||||
mbc->ch = ch;
|
||||
|
||||
err = dect_mbc_setup_tbc(mbc);
|
||||
if (err < 0)
|
||||
goto err2;
|
||||
|
||||
mod_timer(&mbc->timer, jiffies + DECT_MBC_SETUP_TIMEOUT);
|
||||
return 0;
|
||||
err2:
|
||||
dect_mbc_release(mbc);
|
||||
err1:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dect_mbc_conn_indicate(const struct dect_cluster_handle *clh,
|
||||
const struct dect_cell_handle *ch,
|
||||
const struct dect_mbc_id *id)
|
||||
{
|
||||
struct dect_cluster *cl = container_of(clh, struct dect_cluster, handle);
|
||||
struct dect_mbc_id mid;
|
||||
struct dect_mbc *mbc;
|
||||
int err;
|
||||
|
||||
memcpy(&mid, id, sizeof(mid));
|
||||
mid.mcei = dect_mbc_alloc_mcei(cl);
|
||||
|
||||
err = -ENOMEM;
|
||||
mbc = dect_mbc_init(cl, &mid);
|
||||
if (mbc == NULL)
|
||||
goto err1;
|
||||
mbc->ch = ch;
|
||||
|
||||
err = ch->ops->tbc_confirm(ch, &mid);
|
||||
if (err < 0)
|
||||
goto err2;
|
||||
|
||||
mod_timer(&mbc->timer, jiffies + DECT_MBC_SETUP_TIMEOUT);
|
||||
return 0;
|
||||
err2:
|
||||
dect_mbc_release(mbc);
|
||||
err1:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dect_mbc_conn_notify(const struct dect_cluster_handle *clh,
|
||||
const struct dect_mbc_id *id,
|
||||
enum dect_tbc_event event)
|
||||
{
|
||||
struct dect_cluster *cl = container_of(clh, struct dect_cluster, handle);
|
||||
struct dect_mbc *mbc;
|
||||
|
||||
mbc = dect_mbc_get_by_mcei(cl, id->mcei);
|
||||
if (mbc == NULL)
|
||||
return -ENOENT;
|
||||
mbc_debug(mbc, "notify event: %u\n", event);
|
||||
|
||||
switch (event) {
|
||||
case DECT_TBC_SETUP_FAILED:
|
||||
switch (mbc->state) {
|
||||
case DECT_MBC_NONE:
|
||||
return 0;
|
||||
case DECT_MBC_INITIATED:
|
||||
if (++mbc->setup_cnt > DECT_MBC_SETUP_MAX_ATTEMPTS ||
|
||||
dect_mbc_setup_tbc(mbc) < 0)
|
||||
return dect_dlc_mac_conn_disconnect(cl, id->mcei);
|
||||
return 0;
|
||||
default:
|
||||
return WARN_ON(-1);
|
||||
}
|
||||
case DECT_TBC_SETUP_COMPLETE:
|
||||
switch (mbc->state) {
|
||||
case DECT_MBC_NONE:
|
||||
return dect_dlc_mac_conn_indicate(cl, id);
|
||||
case DECT_MBC_INITIATED:
|
||||
return dect_dlc_mac_conn_confirm(cl, id->mcei, id->service);
|
||||
default:
|
||||
return WARN_ON(-1);
|
||||
}
|
||||
case DECT_TBC_HANDSHAKE_TIMEOUT:
|
||||
case DECT_TBC_REMOTE_RELEASE:
|
||||
return dect_dlc_mac_conn_disconnect(cl, id->mcei);
|
||||
default:
|
||||
return WARN_ON(-1);
|
||||
}
|
||||
}
|
||||
|
||||
int dect_mbc_co_data_request(struct dect_cluster *cl, u32 mcei,
|
||||
enum dect_mac_channels chan,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct dect_mbc *mbc;
|
||||
|
||||
mbc = dect_mbc_get_by_mcei(cl, mcei);
|
||||
if (mbc == NULL) {
|
||||
kfree_skb(skb);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
switch (chan) {
|
||||
case DECT_MC_C_S:
|
||||
skb_queue_tail(&mbc->c_tx_queue, skb);
|
||||
break;
|
||||
case DECT_MC_I_N:
|
||||
skb_queue_tail(&mbc->i_tx_queue, skb);
|
||||
break;
|
||||
case DECT_MC_G_F:
|
||||
skb_queue_tail(&mbc->gf_tx_queue, skb);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dect_mbc_data_indicate(const struct dect_cluster_handle *clh,
|
||||
const struct dect_mbc_id *id,
|
||||
enum dect_data_channels chan,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct dect_cluster *cl = container_of(clh, struct dect_cluster, handle);
|
||||
struct dect_mbc *mbc;
|
||||
|
||||
mbc = dect_mbc_get_by_mcei(cl, id->mcei);
|
||||
if (mbc == NULL)
|
||||
goto err;
|
||||
return dect_dlc_mac_co_data_indicate(cl, mbc->id.mcei, chan, skb);
|
||||
|
||||
err:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static void dect_mbc_dtr_indicate(const struct dect_cluster_handle *clh,
|
||||
const struct dect_mbc_id *id,
|
||||
enum dect_data_channels chan)
|
||||
{
|
||||
struct dect_cluster *cl = container_of(clh, struct dect_cluster, handle);
|
||||
struct dect_mbc *mbc;
|
||||
struct sk_buff *skb;
|
||||
|
||||
mbc = dect_mbc_get_by_mcei(cl, id->mcei);
|
||||
if (mbc == NULL)
|
||||
return;
|
||||
|
||||
mbc_debug(mbc, "DTR-indicate\n");
|
||||
skb = dect_dlc_mac_co_dtr_indicate(cl, mbc->id.mcei, chan);
|
||||
if (skb == NULL)
|
||||
return;
|
||||
|
||||
mbc->ch->ops->tbc_data_request(mbc->ch, &mbc->id, chan, skb);
|
||||
mbc->cs_tx_seq++;
|
||||
}
|
||||
|
||||
static void dect_cluster_unbind_cell(struct dect_cluster_handle *clh,
|
||||
struct dect_cell_handle *ch)
|
||||
{
|
||||
list_del(&ch->list);
|
||||
}
|
||||
|
||||
static int dect_cluster_enable_cell(struct dect_cluster *cl,
|
||||
struct dect_cell_handle *ch)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = ch->ops->preload(ch, &cl->pari, ch->rpn, &cl->si);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = ch->ops->enable(ch);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dect_cluster_bind_cell(struct dect_cluster_handle *clh,
|
||||
struct dect_cell_handle *ch)
|
||||
{
|
||||
struct dect_cluster *cl = container_of(clh, struct dect_cluster, handle);
|
||||
u8 rpn, max;
|
||||
int err;
|
||||
|
||||
/* Allocate RPN for the cell */
|
||||
max = 8;
|
||||
for (rpn = 0; rpn < max; rpn++) {
|
||||
if (!dect_cluster_get_cell_by_rpn(cl, rpn))
|
||||
break;
|
||||
}
|
||||
if (rpn == max)
|
||||
return -EMFILE;
|
||||
|
||||
ch->clh = clh;
|
||||
ch->rpn = rpn;
|
||||
|
||||
err = ch->ops->set_mode(ch, cl->mode);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = dect_cluster_enable_cell(cl, ch);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
list_add_tail(&ch->list, &cl->cells);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dect_ccf_ops dect_ccf_ops = {
|
||||
.bind = dect_cluster_bind_cell,
|
||||
.unbind = dect_cluster_unbind_cell,
|
||||
.scan_report = dect_scan_report,
|
||||
.mac_info_indicate = dect_mac_info_indicate,
|
||||
.mbc_conn_indicate = dect_mbc_conn_indicate,
|
||||
.mbc_conn_notify = dect_mbc_conn_notify,
|
||||
.mbc_data_indicate = dect_mbc_data_indicate,
|
||||
.mbc_dtr_indicate = dect_mbc_dtr_indicate,
|
||||
.bmc_page_indicate = dect_bmc_page_indicate,
|
||||
};
|
||||
|
||||
int dect_cluster_scan(struct dect_cluster *cl,
|
||||
const struct dect_llme_req *lreq,
|
||||
const struct dect_ari *pari,
|
||||
const struct dect_ari *pari_mask)
|
||||
{
|
||||
struct dect_cell_handle *ch;
|
||||
|
||||
ch = dect_cluster_get_cell_by_rpn(cl, 0);
|
||||
if (ch == NULL)
|
||||
return -ENOENT;
|
||||
return ch->ops->scan(ch, lreq, pari, pari_mask);
|
||||
}
|
||||
|
||||
static void dect_fp_init_si(struct dect_cluster *cl)
|
||||
{
|
||||
struct dect_si *si = &cl->si;
|
||||
|
||||
/* Make phone not go into "call technician" mode :) */
|
||||
si->ssi.rfcars = 0x3ff;
|
||||
si->fpc.fpc = DECT_FPC_FULL_SLOT |
|
||||
DECT_FPC_CO_SETUP_ON_DUMMY |
|
||||
DECT_FPC_CL_UPLINK |
|
||||
DECT_FPC_CL_DOWNLINK |
|
||||
DECT_FPC_BASIC_A_FIELD_SETUP |
|
||||
DECT_FPC_CF_MESSAGES |
|
||||
DECT_FPC_IN_MIN_DELAY |
|
||||
DECT_FPC_IN_NORM_DELAY |
|
||||
DECT_FPC_IP_ERROR_DETECTION |
|
||||
DECT_FPC_IP_ERROR_CORRECTION;
|
||||
si->fpc.hlc = DECT_HLC_ADPCM_G721_VOICE |
|
||||
DECT_HLC_GAP_PAP_BASIC_SPEECH |
|
||||
DECT_HLC_CISS_SERVICE |
|
||||
DECT_HLC_CLMS_SERVICE |
|
||||
DECT_HLC_COMS_SERVICE;
|
||||
}
|
||||
|
||||
void dect_cluster_init(struct dect_cluster *cl)
|
||||
{
|
||||
spin_lock_init(&cl->lock);
|
||||
INIT_LIST_HEAD(&cl->bmc.bcs);
|
||||
INIT_LIST_HEAD(&cl->mbcs);
|
||||
INIT_LIST_HEAD(&cl->cells);
|
||||
INIT_LIST_HEAD(&cl->mac_connections);
|
||||
if (cl->mode == DECT_MODE_FP)
|
||||
dect_fp_init_si(cl);
|
||||
|
||||
cl->handle.ops = &dect_ccf_ops;
|
||||
cl->handle.index = cl->index;
|
||||
|
||||
// FIXME:
|
||||
if (dect_ccp_cluster_init(cl) < 0)
|
||||
printk("CCP init failed\n");
|
||||
}
|
||||
|
||||
void dect_cluster_shutdown(struct dect_cluster *cl)
|
||||
{
|
||||
struct dect_cell_handle *ch, *next;
|
||||
|
||||
list_for_each_entry_safe(ch, next, &cl->cells, list)
|
||||
dect_cluster_unbind_cell(&cl->handle, ch);
|
||||
dect_ccp_cluster_shutdown(cl);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* DECT RAW sockets
|
||||
*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/dect.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/dect/dect.h>
|
||||
#include <net/dect/mac_csf.h>
|
||||
|
||||
static HLIST_HEAD(dect_raw_sockets);
|
||||
|
||||
struct dect_raw_sk {
|
||||
struct sock sk;
|
||||
};
|
||||
|
||||
static inline struct dect_raw_sk *dect_raw_sk(struct sock *sk)
|
||||
{
|
||||
return (struct dect_raw_sk *)sk;
|
||||
}
|
||||
|
||||
static void __dect_raw_rcv(struct sk_buff *skb)
|
||||
{
|
||||
struct dect_cell *cell = DECT_TRX_CB(skb)->trx->cell;
|
||||
struct hlist_node *node;
|
||||
struct sk_buff *skb2;
|
||||
struct sock *sk;
|
||||
|
||||
sk_for_each_bound(sk, node, &dect_raw_sockets) {
|
||||
if (sk->sk_bound_dev_if &&
|
||||
sk->sk_bound_dev_if != cell->index)
|
||||
continue;
|
||||
|
||||
skb2 = skb_clone(skb, GFP_ATOMIC);
|
||||
if (skb2 == NULL) {
|
||||
sk->sk_err = -ENOMEM;
|
||||
sk->sk_error_report(sk);
|
||||
} else {
|
||||
/* Release the transceiver reference, it is only valid
|
||||
* in IRQ and softirq context.
|
||||
*/
|
||||
DECT_TRX_CB(skb)->trx = NULL;
|
||||
if (dect_sock_queue_rcv_skb(sk, skb2) < 0)
|
||||
kfree_skb(skb2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int dect_raw_release(struct socket *sock)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
|
||||
if (!hlist_unhashed(&sk->sk_bind_node))
|
||||
__sk_del_bind_node(sk);
|
||||
sock_put(sk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dect_raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
|
||||
{
|
||||
struct sockaddr_dect *addr = (struct sockaddr_dect *)uaddr;
|
||||
struct sock *sk = sock->sk;
|
||||
|
||||
if (len < sizeof(*addr) || addr->dect_family != AF_DECT)
|
||||
return -EINVAL;
|
||||
|
||||
if (addr->dect_index != 0 &&
|
||||
!dect_cell_get_by_index(addr->dect_index))
|
||||
return -ENODEV;
|
||||
|
||||
lock_sock(sk);
|
||||
sk->sk_bound_dev_if = addr->dect_index;
|
||||
if (!hlist_unhashed(&sk->sk_bind_node))
|
||||
__sk_del_bind_node(sk);
|
||||
sk_add_bind_node(sk, &dect_raw_sockets);
|
||||
release_sock(sk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dect_raw_getname(struct socket *sock, struct sockaddr *uaddr,
|
||||
int *len, int peer)
|
||||
{
|
||||
struct sockaddr_dect *addr = (struct sockaddr_dect *)uaddr;
|
||||
struct sock *sk = sock->sk;
|
||||
|
||||
if (peer)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
addr->dect_family = AF_DECT;
|
||||
addr->dect_index = sk->sk_bound_dev_if;
|
||||
*len = sizeof(*addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dect_raw_recvmsg(struct kiocb *iocb, struct socket *sock,
|
||||
struct msghdr *msg, size_t len, int flags)
|
||||
{
|
||||
struct sockaddr_dect *addr;
|
||||
struct dect_raw_auxdata aux;
|
||||
struct sock *sk = sock->sk;
|
||||
struct sk_buff *skb;
|
||||
int noblock = flags & MSG_DONTWAIT;
|
||||
size_t copied = 0;
|
||||
int err;
|
||||
|
||||
if (flags & MSG_OOB)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
noblock = flags & MSG_DONTWAIT;
|
||||
skb = skb_recv_datagram(sk, flags, noblock, &err);
|
||||
if (skb == NULL)
|
||||
goto out;
|
||||
|
||||
copied = skb->len;
|
||||
if (len < copied) {
|
||||
msg->msg_flags |= MSG_TRUNC;
|
||||
copied = len;
|
||||
}
|
||||
|
||||
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
|
||||
if (msg->msg_name != NULL) {
|
||||
addr = (struct sockaddr_dect *)msg->msg_name;
|
||||
addr->dect_family = AF_DECT;
|
||||
addr->dect_index = DECT_SK_CB(skb)->index;
|
||||
msg->msg_namelen = sizeof(*addr);
|
||||
}
|
||||
|
||||
sock_recv_timestamp(msg, sk, skb);
|
||||
|
||||
aux.mfn = DECT_TRX_CB(skb)->mfn;
|
||||
aux.frame = DECT_TRX_CB(skb)->frame;
|
||||
aux.slot = DECT_TRX_CB(skb)->slot;
|
||||
aux.rssi = DECT_TRX_CB(skb)->rssi;
|
||||
put_cmsg(msg, SOL_DECT, DECT_RAW_AUXDATA, sizeof(aux), &aux);
|
||||
|
||||
if (flags & MSG_TRUNC)
|
||||
copied = skb->len;
|
||||
out_free:
|
||||
skb_free_datagram(sk, skb);
|
||||
out:
|
||||
return err ? : copied;
|
||||
}
|
||||
|
||||
static const struct proto_ops dect_raw_ops = {
|
||||
.family = PF_DECT,
|
||||
.owner = THIS_MODULE,
|
||||
.release = dect_raw_release,
|
||||
.bind = dect_raw_bind,
|
||||
.connect = sock_no_connect,
|
||||
.socketpair = sock_no_socketpair,
|
||||
.getname = dect_raw_getname,
|
||||
.poll = datagram_poll,
|
||||
.ioctl = sock_no_ioctl,
|
||||
.listen = sock_no_listen,
|
||||
.shutdown = sock_no_shutdown,
|
||||
.setsockopt = sock_no_setsockopt,
|
||||
.getsockopt = sock_no_getsockopt,
|
||||
.sendmsg = sock_no_sendmsg,
|
||||
.recvmsg = dect_raw_recvmsg,
|
||||
.mmap = sock_no_mmap,
|
||||
.sendpage = sock_no_sendpage,
|
||||
};
|
||||
|
||||
static struct dect_proto dect_raw_proto = {
|
||||
.type = SOCK_RAW,
|
||||
.protocol = DECT_RAW,
|
||||
.capability = CAP_NET_RAW,
|
||||
.ops = &dect_raw_ops,
|
||||
.proto.name = "DECT_RAW",
|
||||
.proto.owner = THIS_MODULE,
|
||||
.proto.obj_size = sizeof(struct dect_raw_sk),
|
||||
};
|
||||
|
||||
static int __init dect_raw_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = dect_proto_register(&dect_raw_proto);
|
||||
if (err < 0)
|
||||
return err;
|
||||
rcu_assign_pointer(dect_raw_rcv_hook, __dect_raw_rcv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit dect_raw_exit(void)
|
||||
{
|
||||
rcu_assign_pointer(dect_raw_rcv_hook, NULL);
|
||||
synchronize_rcu();
|
||||
dect_proto_unregister(&dect_raw_proto);
|
||||
}
|
||||
|
||||
module_init(dect_raw_init);
|
||||
module_exit(dect_raw_exit);
|
||||
|
||||
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
|
||||
MODULE_DESCRIPTION("DECT RAW sockets");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
MODULE_ALIAS_NET_PF_PROTO(PF_DECT, DECT_RAW);
|
|
@ -0,0 +1,724 @@
|
|||
/*
|
||||
* DECT transceiver and transceiver group functions
|
||||
*
|
||||
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
//#define DEBUG
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <net/dect/dect.h>
|
||||
#include <net/dect/mac_csf.h>
|
||||
#include <net/dect/transceiver.h>
|
||||
|
||||
static RAW_NOTIFIER_HEAD(dect_transceiver_chain);
|
||||
LIST_HEAD(dect_transceiver_list);
|
||||
|
||||
#define trx_debug(trx, fmt, args...) \
|
||||
pr_debug("%s: " fmt, trx->name, ## args)
|
||||
|
||||
static const struct dect_band *dect_band[DECT_BAND_NUM];
|
||||
static const u8 dect_pkt_size[] = {
|
||||
[DECT_PACKET_P00] = DECT_P00_SIZE,
|
||||
[DECT_PACKET_P08] = DECT_P08_SIZE,
|
||||
[DECT_PACKET_P32] = DECT_P32_SIZE,
|
||||
[DECT_PACKET_P80] = DECT_P80_SIZE,
|
||||
};
|
||||
|
||||
/**
|
||||
* dect_transceiver_alloc_skb - allocate a transceiver RX skb
|
||||
*
|
||||
* @trx: transceiver
|
||||
* @slot: slot number
|
||||
*
|
||||
* Allocate a skb according to the receiving channel characteristics.
|
||||
*/
|
||||
struct sk_buff *dect_transceiver_alloc_skb(struct dect_transceiver *trx, u8 slot)
|
||||
{
|
||||
const struct dect_transceiver_slot *ts = &trx->slots[slot];
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = alloc_skb(dect_pkt_size[ts->chd.pkt], GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
return NULL;
|
||||
DECT_TRX_CB(skb)->trx = trx;
|
||||
DECT_TRX_CB(skb)->slot = ts->chd.slot;
|
||||
/* Reserve room for preamble and set up adjacent packet data pointer */
|
||||
skb_reserve(skb, DECT_PREAMBLE_SIZE);
|
||||
skb_put(skb, dect_pkt_size[ts->chd.pkt] - DECT_PREAMBLE_SIZE);
|
||||
return skb;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dect_transceiver_alloc_skb);
|
||||
|
||||
/* Transceiver virtual clock maintenance:
|
||||
*
|
||||
* The transceiver layer processes the received frames from some slots in
|
||||
* the past while the transceiver is transceiving on the following slots.
|
||||
* Additionally the transceiver needs to be maintained for the upcoming
|
||||
* slots. Therefore there are three different reference frames of time:
|
||||
*
|
||||
* [ RX ][ TRX ][ TX ]
|
||||
* slot_0 -> slot_23
|
||||
*
|
||||
* - Real time, which is only known to the transceiver
|
||||
*
|
||||
* - RX time, which is a virtual clock following real time by at least one
|
||||
* slot and advancing as received slots are processed. A transceiver must
|
||||
* generate enough events so that the RX time never lags behind the TRX
|
||||
* time for more than one TDMA half frame.
|
||||
*
|
||||
* - TX time, which is a virtual clock leading RX time by a usually constant
|
||||
* amount of slots large enough so that packets queued to the transceiver
|
||||
* will reach the transceiver in time to be sent. It always leads real time,
|
||||
* but must never have a distance greater than one TDMA half frame from RX
|
||||
* time, resulting in a maximal distance of 11 to real time.
|
||||
*
|
||||
* Transceivers periodically notify the transceiver layer of an elapsed amount
|
||||
* of time and the frames that were received during that period. The events
|
||||
* batches generated by multiple transceivers contained in a group are merged
|
||||
* and processed as a single chronological event stream.
|
||||
*
|
||||
* The following steps are performed during processing (for every slot):
|
||||
*
|
||||
* - Received packets are passed to the MAC layer
|
||||
* - A RX tick is generated, ending the last RX slot
|
||||
* - A TX tick with some offset in the future is generated, beginning the
|
||||
* next TX slot
|
||||
*
|
||||
* The RX and TX ticks are used by the MAC layer to maintain two timer bases
|
||||
* for performing maintenance operations after a slot was received or before
|
||||
* a slot will be transmitted.
|
||||
*/
|
||||
|
||||
/**
|
||||
* dect_transceiver_queue_event - queue a transceiver event for BH processing
|
||||
*
|
||||
* @trx: DECT transceiver
|
||||
* @event: Transceiver event
|
||||
*/
|
||||
void dect_transceiver_queue_event(struct dect_transceiver *trx,
|
||||
struct dect_transceiver_event *event)
|
||||
{
|
||||
struct dect_transceiver_group *grp = &trx->cell->trg;
|
||||
|
||||
spin_lock(&grp->lock);
|
||||
list_add_tail(&event->list, &grp->events);
|
||||
spin_unlock(&grp->lock);
|
||||
|
||||
tasklet_hi_schedule(&grp->tasklet);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dect_transceiver_queue_event);
|
||||
|
||||
static struct dect_transceiver_event *
|
||||
dect_dequeue_event(struct dect_transceiver_group *grp)
|
||||
{
|
||||
struct dect_transceiver_event *event;
|
||||
unsigned long flags;
|
||||
|
||||
event = NULL;
|
||||
spin_lock_irqsave(&grp->lock, flags);
|
||||
if (!list_empty(&grp->events)) {
|
||||
event = list_first_entry(&grp->events,
|
||||
struct dect_transceiver_event,
|
||||
list);
|
||||
list_del(&event->list);
|
||||
}
|
||||
spin_unlock_irqrestore(&grp->lock, flags);
|
||||
return event;
|
||||
}
|
||||
|
||||
static void dect_tg_merge_events(struct dect_transceiver_group *grp,
|
||||
struct dect_transceiver *trx,
|
||||
struct dect_transceiver_event *event)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
u8 slot, idx, i;
|
||||
|
||||
/* Transfer the packets to the slot input queues and mark the
|
||||
* slot events.
|
||||
*/
|
||||
trx_debug(trx, "merge %u events pos %u rssi_mask %x\n",
|
||||
trx->ops->eventrate, event->slotpos, event->rssi_mask);
|
||||
|
||||
for (i = 0; i < trx->ops->eventrate; i++) {
|
||||
slot = event->slotpos + i;
|
||||
idx = slot % ARRAY_SIZE(grp->slots);
|
||||
|
||||
skb = skb_peek(&event->rx_queue);
|
||||
if (skb != NULL && DECT_TRX_CB(skb)->slot == slot) {
|
||||
__skb_unlink(skb, &event->rx_queue);
|
||||
__skb_queue_tail(&grp->slots[idx].queue, skb);
|
||||
} else if (event->rssi_mask & (1 << i))
|
||||
grp->slots[idx].rssi[trx->index] = event->rssi[i];
|
||||
|
||||
grp->slots[idx].mask |= 1 << trx->index;
|
||||
}
|
||||
}
|
||||
|
||||
static bool seqno_before(u32 seq1, u32 seq2)
|
||||
{
|
||||
return (s32)(seq2 - seq1) > 0;
|
||||
}
|
||||
|
||||
static bool seqno_after(u32 seq1, u32 seq2)
|
||||
{
|
||||
return seqno_before(seq2, seq1);
|
||||
}
|
||||
|
||||
static void dect_tg_process_events(struct dect_transceiver_group *grp)
|
||||
{
|
||||
struct dect_transceiver *trx;
|
||||
struct dect_transceiver_slot *ts;
|
||||
struct sk_buff *skb;
|
||||
u8 idx, d, i;
|
||||
u16 late;
|
||||
|
||||
pr_debug("process events slot_low=%u slot_high=%u distance=%u\n",
|
||||
grp->slot_low, grp->slot_high,
|
||||
dect_slot_distance(grp->slot_low, grp->slot_high));
|
||||
|
||||
while (grp->slot_low != grp->slot_high) {
|
||||
/*
|
||||
* If more than one half frame is missing, only forward the
|
||||
* clock since the slot positions refer to slots in the
|
||||
* following half frame.
|
||||
*/
|
||||
d = dect_slot_distance(grp->slot_low, grp->slot_high);
|
||||
if (d > ARRAY_SIZE(grp->slots))
|
||||
goto tick;
|
||||
|
||||
idx = grp->slot_low % ARRAY_SIZE(grp->slots);
|
||||
|
||||
/* Check for transceivers which are lagging by more than their
|
||||
* event rate window and mark the current window entirely as
|
||||
* lost.
|
||||
*/
|
||||
late = grp->slots[idx].mask ^ grp->trxmask;
|
||||
while (late != 0) {
|
||||
trx = grp->trx[ffs(late) - 1];
|
||||
late &= ~(1 << trx->index);
|
||||
|
||||
if (!seqno_before(trx->seqno + trx->ops->eventrate,
|
||||
grp->seqno) &&
|
||||
d <= trx->ops->eventrate)
|
||||
continue;
|
||||
|
||||
trx_debug(trx, "late for window %u\n", grp->slot_low);
|
||||
for (i = 0; i < trx->ops->eventrate; i++)
|
||||
grp->slots[(idx + i) % 12].mask |= 1 << trx->index;
|
||||
trx->stats.event_late++;
|
||||
}
|
||||
|
||||
if (grp->slots[idx].mask != grp->trxmask) {
|
||||
pr_debug("slot %u incomplete: mask %x trx %x\n",
|
||||
grp->slot_low, grp->slots[idx].mask, grp->trxmask);
|
||||
break;
|
||||
}
|
||||
|
||||
while ((skb = __skb_dequeue(&grp->slots[idx].queue))) {
|
||||
trx = DECT_TRX_CB(skb)->trx;
|
||||
ts = &trx->slots[DECT_TRX_CB(skb)->slot];
|
||||
dect_mac_rcv(trx, ts, skb);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(grp->slots[idx].rssi); i++) {
|
||||
if (grp->slots[idx].rssi[i] == 0)
|
||||
continue;
|
||||
trx = grp->trx[i];
|
||||
ts = &trx->slots[grp->slot_low];
|
||||
dect_mac_report_rssi(trx, ts, grp->slots[idx].rssi[i]);
|
||||
grp->slots[idx].rssi[i] = 0;
|
||||
}
|
||||
|
||||
grp->slots[idx].mask = 0;
|
||||
tick:
|
||||
dect_mac_rx_tick(grp, dect_next_slotnum(grp->slot_low));
|
||||
dect_mac_tx_tick(grp, dect_slot_add(grp->slot_low,
|
||||
2 * grp->latency));
|
||||
|
||||
grp->slot_low = dect_next_slotnum(grp->slot_low);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Softirq transceiver group event processing
|
||||
*/
|
||||
static void dect_transceiver_tasklet(unsigned long data)
|
||||
{
|
||||
struct dect_transceiver_group *grp = (struct dect_transceiver_group *)data;
|
||||
struct dect_transceiver *trx;
|
||||
struct dect_transceiver_event *event;
|
||||
struct sk_buff *skb;
|
||||
|
||||
again:
|
||||
event = dect_dequeue_event(grp);
|
||||
if (event == NULL)
|
||||
return;
|
||||
trx = event->trx;
|
||||
|
||||
trx_debug(trx, "event handler: trx: seq %u pos %u grp: seq %u pos %u\n",
|
||||
trx->seqno, event->slotpos, grp->seqno, grp->slot_low);
|
||||
|
||||
/* Before a transceiver is locked, its timing might vary and isn't
|
||||
* synchronized to the remaining group. The MAC layer handles this
|
||||
* manually.
|
||||
*/
|
||||
if (trx->state != DECT_TRANSCEIVER_LOCKED) {
|
||||
skb = __skb_dequeue(&event->rx_queue);
|
||||
if (skb != NULL)
|
||||
dect_mac_irc_rcv(trx, skb);
|
||||
dect_mac_irc_tick(trx);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If a secondary transceiver enters locked state or a tranceiver missed
|
||||
* a previous window, its sequence number is out of sync. Resync it once
|
||||
* it starts reporting events in the current window.
|
||||
*
|
||||
* FIXME: driver should ignore frames with missed interrupts completely
|
||||
* FIXME2: off by one window if receiver is not first
|
||||
*/
|
||||
if (seqno_before(trx->seqno + trx->ops->eventrate, grp->seqno)) {
|
||||
if (event->slotpos != grp->slot_low) {
|
||||
trx_debug(trx, "unsynchronized\n");
|
||||
__skb_queue_purge(&event->rx_queue);
|
||||
goto out;
|
||||
}
|
||||
trx->seqno = grp->seqno;
|
||||
trx_debug(trx, "synchronized to seqno %u\n", trx->seqno);
|
||||
}
|
||||
|
||||
/* Merge the events and update the sequence number. The transceiver
|
||||
* with the highest sequence number determines the slot position for
|
||||
* the entire group.
|
||||
*/
|
||||
dect_tg_merge_events(grp, trx, event);
|
||||
|
||||
trx->seqno += trx->ops->eventrate;
|
||||
if (seqno_after(trx->seqno, grp->seqno)) {
|
||||
grp->seqno = trx->seqno;
|
||||
grp->slot_high =
|
||||
dect_slot_add(event->slotpos, trx->ops->eventrate);
|
||||
}
|
||||
|
||||
dect_tg_process_events(grp);
|
||||
|
||||
out:
|
||||
dect_release_transceiver_event(event);
|
||||
goto again;
|
||||
}
|
||||
|
||||
int dect_transceiver_group_add(struct dect_transceiver_group *grp,
|
||||
struct dect_transceiver *trx)
|
||||
{
|
||||
u8 index;
|
||||
|
||||
index = ffz(grp->trxmask);
|
||||
if (index >= ARRAY_SIZE(grp->trx))
|
||||
return -EMFILE;
|
||||
|
||||
trx->index = index;
|
||||
grp->trx[index] = trx;
|
||||
if (trx->ops->latency > grp->latency)
|
||||
grp->latency = trx->ops->latency;
|
||||
|
||||
grp->trxmask |= 1 << index;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dect_transceiver_group_remove(struct dect_transceiver_group *grp,
|
||||
struct dect_transceiver *trx)
|
||||
{
|
||||
grp->trxmask &= ~(1 << trx->index);
|
||||
/* Synchronize with interrupt and softirq processing */
|
||||
synchronize_rcu();
|
||||
grp->trx[trx->index] = NULL;
|
||||
}
|
||||
|
||||
void dect_transceiver_group_init(struct dect_transceiver_group *grp)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
spin_lock_init(&grp->lock);
|
||||
INIT_LIST_HEAD(&grp->events);
|
||||
for (i = 0; i < ARRAY_SIZE(grp->slots); i++)
|
||||
__skb_queue_head_init(&grp->slots[i].queue);
|
||||
|
||||
tasklet_init(&grp->tasklet, dect_transceiver_tasklet,
|
||||
(unsigned long)grp);
|
||||
}
|
||||
|
||||
void dect_transceiver_disable(struct dect_transceiver *trx)
|
||||
{
|
||||
trx->ops->disable(trx);
|
||||
trx->state = DECT_TRANSCEIVER_STOPPED;
|
||||
}
|
||||
|
||||
void dect_transceiver_enable(struct dect_transceiver *trx)
|
||||
{
|
||||
if (trx->mode == DECT_TRANSCEIVER_MASTER)
|
||||
trx->state = DECT_TRANSCEIVER_LOCKED;
|
||||
else {
|
||||
trx->state = DECT_TRANSCEIVER_UNLOCKED;
|
||||
trx->slots[DECT_SCAN_SLOT].state = DECT_SLOT_SCANNING;
|
||||
}
|
||||
trx->ops->enable(trx);
|
||||
}
|
||||
|
||||
void dect_transceiver_confirm(struct dect_transceiver *trx)
|
||||
{
|
||||
trx_debug(trx, "confirm\n");
|
||||
trx->state = DECT_TRANSCEIVER_LOCK_PENDING;
|
||||
trx->slots[DECT_SCAN_SLOT].state = DECT_SLOT_RX;
|
||||
trx->ops->confirm(trx);
|
||||
}
|
||||
|
||||
void dect_transceiver_unlock(struct dect_transceiver *trx)
|
||||
{
|
||||
trx_debug(trx, "unlock\n");
|
||||
trx->ops->unlock(trx);
|
||||
trx->slots[DECT_SCAN_SLOT].state = DECT_SLOT_SCANNING;
|
||||
trx->state = DECT_TRANSCEIVER_UNLOCKED;
|
||||
}
|
||||
|
||||
int dect_transceiver_set_band(struct dect_transceiver *trx, u8 bandnum)
|
||||
{
|
||||
const struct dect_band *band;
|
||||
|
||||
band = dect_band[bandnum];
|
||||
if (band == NULL)
|
||||
return -ENOENT;
|
||||
trx->carriers = trx->ops->set_band(trx, band);
|
||||
trx->band = band;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dect_transceiver_lock(struct dect_transceiver *trx, u8 slot)
|
||||
{
|
||||
trx_debug(trx, "lock to slot %u\n", slot);
|
||||
trx->slots[DECT_SCAN_SLOT].state = DECT_SLOT_IDLE;
|
||||
trx->state = DECT_TRANSCEIVER_LOCKED;
|
||||
trx->ops->lock(trx, slot);
|
||||
}
|
||||
|
||||
bool dect_transceiver_channel_available(const struct dect_transceiver *trx,
|
||||
const struct dect_channel_desc *chd)
|
||||
{
|
||||
if (trx->slots[chd->slot].state == DECT_SLOT_RX ||
|
||||
trx->slots[chd->slot].state == DECT_SLOT_TX)
|
||||
return false;
|
||||
|
||||
switch ((int)chd->pkt) {
|
||||
case DECT_PACKET_P80:
|
||||
if (trx->blind_full_slots & (1 << (chd->slot + 1)))
|
||||
return false;
|
||||
case DECT_PACKET_P32:
|
||||
case DECT_PACKET_P08:
|
||||
case DECT_PACKET_P00:
|
||||
if (trx->blind_full_slots & (1 << chd->slot))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dect_tg_update_blind_full_slots(struct dect_transceiver_group *trg)
|
||||
{
|
||||
const struct dect_transceiver *trx;
|
||||
u32 blind_full_slots;
|
||||
|
||||
blind_full_slots = (~0U) & DECT_SLOT_MASK;
|
||||
dect_foreach_transceiver(trx, trg)
|
||||
blind_full_slots &= trx->blind_full_slots;
|
||||
|
||||
if (trg->blind_full_slots != blind_full_slots) {
|
||||
trg->blind_full_slots = blind_full_slots;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* dect_transceiver_reserve - reserve transceiver resources for a physical channel
|
||||
*
|
||||
* Reserve the slot positions necessary to estabish the specified physical
|
||||
* channel. The chosen transceivers and the global groups blind full slot
|
||||
* masks are updated.
|
||||
*
|
||||
* Returns true when the global visibility state has changed.
|
||||
*/
|
||||
bool dect_transceiver_reserve(struct dect_transceiver_group *trg,
|
||||
struct dect_transceiver *trx,
|
||||
const struct dect_channel_desc *chd)
|
||||
{
|
||||
switch ((int)chd->pkt) {
|
||||
case DECT_PACKET_P80:
|
||||
trx->blind_full_slots |= 1 << (chd->slot + 1);
|
||||
case DECT_PACKET_P32:
|
||||
case DECT_PACKET_P08:
|
||||
case DECT_PACKET_P00:
|
||||
trx->blind_full_slots |= 1 << chd->slot;
|
||||
break;
|
||||
}
|
||||
|
||||
return dect_tg_update_blind_full_slots(trg);
|
||||
}
|
||||
|
||||
/**
|
||||
* dect_transceiver_release - release transceiver resources of a phyiscal channel
|
||||
*
|
||||
* Release the slot positions used by the specified physical channel. The
|
||||
* transceiver and the global group blind full slot masks are updated.
|
||||
*
|
||||
* Returns true when the global visibility state has changed.
|
||||
*/
|
||||
bool dect_transceiver_release(struct dect_transceiver_group *trg,
|
||||
struct dect_transceiver *trx,
|
||||
const struct dect_channel_desc *chd)
|
||||
{
|
||||
switch ((int)chd->pkt) {
|
||||
case DECT_PACKET_P80:
|
||||
trx->blind_full_slots &= ~(1 << (chd->slot + 1));
|
||||
case DECT_PACKET_P32:
|
||||
case DECT_PACKET_P08:
|
||||
case DECT_PACKET_P00:
|
||||
trx->blind_full_slots &= ~(1 << chd->slot);
|
||||
break;
|
||||
}
|
||||
|
||||
return dect_tg_update_blind_full_slots(trg);
|
||||
}
|
||||
|
||||
void dect_register_notifier(struct notifier_block *nb)
|
||||
{
|
||||
dect_lock();
|
||||
raw_notifier_chain_register(&dect_transceiver_chain, nb);
|
||||
dect_unlock();
|
||||
}
|
||||
|
||||
void dect_unregister_notifier(struct notifier_block *nb)
|
||||
{
|
||||
dect_lock();
|
||||
raw_notifier_chain_unregister(&dect_transceiver_chain, nb);
|
||||
dect_unlock();
|
||||
}
|
||||
|
||||
static void dect_transceiver_notify(unsigned long val,
|
||||
struct dect_transceiver *trx)
|
||||
{
|
||||
raw_notifier_call_chain(&dect_transceiver_chain, val, trx);
|
||||
}
|
||||
|
||||
struct dect_transceiver *dect_transceiver_alloc(const struct dect_transceiver_ops *ops,
|
||||
unsigned int priv)
|
||||
{
|
||||
struct dect_transceiver *trx;
|
||||
unsigned int nevents, size, i;
|
||||
|
||||
/* Allocate enough event structures for one TDMA half frame */
|
||||
nevents = DECT_HALF_FRAME_SIZE / ops->eventrate;
|
||||
size = nevents * sizeof(trx->event[0]) + priv;
|
||||
|
||||
trx = kzalloc(sizeof(*trx) + size, GFP_KERNEL);
|
||||
if (trx == NULL)
|
||||
return NULL;
|
||||
|
||||
trx->state = DECT_TRANSCEIVER_STOPPED;
|
||||
trx->ops = ops;
|
||||
trx->blind_full_slots = ~ops->slotmask & DECT_SLOT_MASK;
|
||||
for (i = 0; i < DECT_FRAME_SIZE; i++)
|
||||
trx->slots[i].chd.slot = i;
|
||||
|
||||
for (i = 0; i < nevents; i++) {
|
||||
skb_queue_head_init(&trx->event[i].rx_queue);
|
||||
trx->event[i].trx = trx;
|
||||
}
|
||||
return trx;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dect_transceiver_alloc);
|
||||
|
||||
void dect_transceiver_free(struct dect_transceiver *trx)
|
||||
{
|
||||
kfree(trx);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dect_transceiver_free);
|
||||
|
||||
static int dect_transceiver_alloc_name(struct dect_transceiver *trx)
|
||||
{
|
||||
struct dect_transceiver *t;
|
||||
unsigned long *inuse;
|
||||
int i;
|
||||
|
||||
inuse = (unsigned long *)get_zeroed_page(GFP_KERNEL);
|
||||
if (inuse == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
list_for_each_entry(t, &dect_transceiver_list, list) {
|
||||
if (!sscanf(t->name, "trx%d", &i))
|
||||
continue;
|
||||
if (i > BITS_PER_BYTE * PAGE_SIZE)
|
||||
continue;
|
||||
set_bit(i, inuse);
|
||||
}
|
||||
|
||||
i = find_first_zero_bit(inuse, BITS_PER_BYTE * PAGE_SIZE);
|
||||
free_page((unsigned long)inuse);
|
||||
if (i == BITS_PER_BYTE * PAGE_SIZE)
|
||||
return -ENFILE;
|
||||
|
||||
snprintf(trx->name, sizeof(trx->name), "trx%d", i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dect_register_transceiver(struct dect_transceiver *trx)
|
||||
{
|
||||
int err;
|
||||
|
||||
dect_lock();
|
||||
err = dect_transceiver_alloc_name(trx);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = dect_transceiver_set_band(trx, DECT_DEFAULT_BAND);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
list_add_tail(&trx->list, &dect_transceiver_list);
|
||||
dect_transceiver_notify(DECT_TRANSCEIVER_REGISTER, trx);
|
||||
out:
|
||||
dect_unlock();
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dect_register_transceiver);
|
||||
|
||||
void dect_unregister_transceiver(struct dect_transceiver *trx)
|
||||
{
|
||||
dect_lock();
|
||||
list_del(&trx->list);
|
||||
dect_transceiver_notify(DECT_TRANSCEIVER_UNREGISTER, trx);
|
||||
dect_unlock();
|
||||
|
||||
synchronize_rcu();
|
||||
trx->ops->destructor(trx);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dect_unregister_transceiver);
|
||||
|
||||
/*
|
||||
* RF-bands:
|
||||
*/
|
||||
|
||||
struct dect_band_desc {
|
||||
u8 band;
|
||||
u8 carriers;
|
||||
s16 c1_off;
|
||||
u8 c2;
|
||||
s16 c2_off;
|
||||
};
|
||||
|
||||
static const struct dect_band_desc dect_band_desc[] __initdata = {
|
||||
/* 1880 MHz to 1900 MHz RF band 00000 */
|
||||
{ 0, 10, 0, -1, 0, },
|
||||
/* 1880 MHz to 1978 MHz and 2010 MHz to 2025 MHz RF band 00001 */
|
||||
{ 1, 64, 0, 56, 19, },
|
||||
/* 1880 MHz to 1925 MHz and 2010 MHz to 2025 MHz RF band 00010 */
|
||||
{ 2, 33, 0, 25, 50, },
|
||||
/* 1880 MHz to 1900 MHz, 1915 MHz to 1940 MHz and 2010 MHz to
|
||||
* 2025 MHz RF band 00011 */
|
||||
{ 3, 33, 10, 25, 50, },
|
||||
/* 1880 MHz to 1900 MHz, 1935 MHz to 1960 MHz and 2010 MHz to
|
||||
* 2025 MHz RF band 00100 */
|
||||
{ 4, 33, 22, 25, 50, },
|
||||
/* 1880 MHz to 1900 MHZ, 1955 MHz to 1980 MHz and 2010 MHz to
|
||||
* 2025 MHz RF band 00101 */
|
||||
{ 5, 33, 34, 25, 50, },
|
||||
/* 902 MHz to 928 MHz RF band 01000 */
|
||||
{ 8, 24, -576, -1, 0, },
|
||||
/* 2400 MHz to 2483,5 MHz RF band 01001 */
|
||||
{ 9, 55, 292, -1, 0, },
|
||||
};
|
||||
|
||||
/*
|
||||
* A nominal DECT RF carrier is one whose center frequency is generated by
|
||||
* the formula:
|
||||
*
|
||||
* Fg = F0 - g × 1,728 MHz, where g is any integer
|
||||
*/
|
||||
static u32 __init dect_calc_frequency(s16 g)
|
||||
{
|
||||
if (g >= 0 && g < 10)
|
||||
return DECT_FREQUENCY_F0 - g * DECT_CARRIER_WIDTH;
|
||||
else
|
||||
return DECT_FREQUENCY_F0 + (g - 9) * DECT_CARRIER_WIDTH;
|
||||
}
|
||||
|
||||
static int __init dect_init_band(const struct dect_band_desc *desc)
|
||||
{
|
||||
struct dect_band *band;
|
||||
unsigned int size;
|
||||
u8 carrier;
|
||||
|
||||
size = sizeof(*band) + desc->carriers * sizeof(band->frequency[0]);
|
||||
band = kmalloc(size, GFP_KERNEL);
|
||||
if (band == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
band->band = desc->band;
|
||||
band->carriers = desc->carriers;
|
||||
|
||||
for (carrier = 0; carrier < 10; carrier++)
|
||||
band->frequency[carrier] =
|
||||
dect_calc_frequency(carrier);
|
||||
|
||||
for (; carrier < min(desc->carriers, desc->c2); carrier++)
|
||||
band->frequency[carrier] =
|
||||
dect_calc_frequency(carrier + desc->c1_off);
|
||||
|
||||
for (; carrier < desc->carriers; carrier++)
|
||||
band->frequency[carrier] =
|
||||
dect_calc_frequency(carrier + desc->c2_off);
|
||||
|
||||
printk("RF-band %u:\n", band->band);
|
||||
for (carrier = 0; carrier < band->carriers; carrier++) {
|
||||
printk(" carrier %u: %u.%.3uMHz\n", carrier,
|
||||
band->frequency[carrier] / 1000,
|
||||
band->frequency[carrier] % 1000);
|
||||
}
|
||||
printk("\n");
|
||||
|
||||
dect_band[band->band] = band;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init dect_transceiver_module_init(void)
|
||||
{
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dect_band_desc); i++) {
|
||||
err = dect_init_band(&dect_band_desc[i]);
|
||||
if (err < 0)
|
||||
goto err1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
dect_transceiver_module_exit();
|
||||
return err;
|
||||
}
|
||||
|
||||
void dect_transceiver_module_exit(void)
|
||||
{
|
||||
u8 band;
|
||||
|
||||
for (band = 0; band < ARRAY_SIZE(dect_band); band++)
|
||||
kfree(dect_band[band]);
|
||||
}
|
Reference in New Issue