diff options
Diffstat (limited to 'src/host/libosmocom')
-rw-r--r-- | src/host/libosmocom/README | 1 | ||||
-rw-r--r-- | src/host/libosmocom/include/osmocom/debug.h | 131 | ||||
-rw-r--r-- | src/host/libosmocom/include/osmocom/gsm_04_08.h | 796 | ||||
-rw-r--r-- | src/host/libosmocom/include/osmocom/gsmtap.h | 60 | ||||
-rw-r--r-- | src/host/libosmocom/include/osmocom/linuxlist.h | 360 | ||||
-rw-r--r-- | src/host/libosmocom/include/osmocom/meas_rep.h | 85 | ||||
-rw-r--r-- | src/host/libosmocom/include/osmocom/msgb.h | 114 | ||||
-rw-r--r-- | src/host/libosmocom/include/osmocom/select.h | 22 | ||||
-rw-r--r-- | src/host/libosmocom/include/osmocom/talloc.h | 192 | ||||
-rw-r--r-- | src/host/libosmocom/include/osmocom/timer.h | 72 | ||||
-rw-r--r-- | src/host/libosmocom/src/debug.c | 463 | ||||
-rw-r--r-- | src/host/libosmocom/src/msgb.c | 97 | ||||
-rw-r--r-- | src/host/libosmocom/src/select.c | 124 | ||||
-rw-r--r-- | src/host/libosmocom/src/signal.c | 83 | ||||
-rw-r--r-- | src/host/libosmocom/src/talloc.c | 1805 | ||||
-rw-r--r-- | src/host/libosmocom/src/timer.c | 185 |
16 files changed, 4590 insertions, 0 deletions
diff --git a/src/host/libosmocom/README b/src/host/libosmocom/README new file mode 100644 index 00000000..1a97beac --- /dev/null +++ b/src/host/libosmocom/README @@ -0,0 +1 @@ +This is a copy of OpenBSC header and sources. Do not edit them. diff --git a/src/host/libosmocom/include/osmocom/debug.h b/src/host/libosmocom/include/osmocom/debug.h new file mode 100644 index 00000000..c40eec3f --- /dev/null +++ b/src/host/libosmocom/include/osmocom/debug.h @@ -0,0 +1,131 @@ +#ifndef _DEBUG_H +#define _DEBUG_H + +#include <stdio.h> +#include "linuxlist.h" + +#define DEBUG + +/* Debug Areas of the code */ +enum { + DRLL, + DCC, + DMM, + DRR, + DRSL, + DNM, + DMNCC, + DSMS, + DPAG, + DMEAS, + DMI, + DMIB, + DMUX, + DINP, + DSCCP, + DMSC, + DMGCP, + DHO, + DDB, + DREF, + Debug_LastEntry, +}; + +#ifdef DEBUG +#define DEBUGP(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 0, fmt, ## args) +#define DEBUGPC(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 1, fmt, ## args) +#else +#define DEBUGP(xss, fmt, args...) +#define DEBUGPC(ss, fmt, args...) +#endif + + +#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1]; + +char *hexdump(const unsigned char *buf, int len); +void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 5, 6))); + +/* new logging interface */ +#define LOGP(ss, level, fmt, args...) debugp2(ss, level, __FILE__, __LINE__, 0, fmt, ##args) +#define LOGPC(ss, level, fmt, args...) debugp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args) + +/* different levels */ +#define LOGL_DEBUG 1 /* debugging information */ +#define LOGL_INFO 3 +#define LOGL_NOTICE 5 /* abnormal/unexpected condition */ +#define LOGL_ERROR 7 /* error condition, requires user action */ +#define LOGL_FATAL 8 /* fatal, program aborted */ + +/* context */ +#define BSC_CTX_LCHAN 0 +#define BSC_CTX_SUBSCR 1 +#define BSC_CTX_BTS 2 +#define BSC_CTX_SCCP 3 + +/* target */ + +enum { + DEBUG_FILTER_IMSI = 1 << 0, + DEBUG_FILTER_ALL = 1 << 1, +}; + +struct debug_category { + int enabled; + int loglevel; +}; + +struct debug_target { + int filter_map; + char *imsi_filter; + + + struct debug_category categories[Debug_LastEntry]; + int use_color; + int print_timestamp; + int loglevel; + + union { + struct { + FILE *out; + } tgt_stdout; + + struct { + int priority; + } tgt_syslog; + + struct { + void *vty; + } tgt_vty; + }; + + void (*output) (struct debug_target *target, const char *string); + + struct llist_head entry; +}; + +/* use the above macros */ +void debugp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 6, 7))); +void debug_init(void); + +/* context management */ +void debug_reset_context(void); +void debug_set_context(int ctx, void *value); + +/* filter on the targets */ +void debug_set_imsi_filter(struct debug_target *target, const char *imsi); +void debug_set_all_filter(struct debug_target *target, int); +void debug_set_use_color(struct debug_target *target, int); +void debug_set_print_timestamp(struct debug_target *target, int); +void debug_set_log_level(struct debug_target *target, int log_level); +void debug_parse_category_mask(struct debug_target *target, const char* mask); +int debug_parse_level(const char *lvl); +int debug_parse_category(const char *category); +void debug_set_category_filter(struct debug_target *target, int category, int enable, int level); + + +/* management of the targets */ +struct debug_target *debug_target_create(void); +struct debug_target *debug_target_create_stderr(void); +void debug_add_target(struct debug_target *target); +void debug_del_target(struct debug_target *target); +#endif /* _DEBUG_H */ diff --git a/src/host/libosmocom/include/osmocom/gsm_04_08.h b/src/host/libosmocom/include/osmocom/gsm_04_08.h new file mode 100644 index 00000000..84b9b5d1 --- /dev/null +++ b/src/host/libosmocom/include/osmocom/gsm_04_08.h @@ -0,0 +1,796 @@ +#ifndef _GSM_04_08_H +#define _GSM_04_08_H + +#include <osmocom/meas_rep.h> + +/* GSM TS 04.08 definitions */ +struct gsm_lchan; + +struct gsm48_classmark1 { + u_int8_t spare:1, + rev_level:2, + es_ind:1, + a5_1:1, + pwr_lev:3; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.5 */ +struct gsm48_chan_desc { + u_int8_t chan_nr; + union { + struct { + u_int8_t maio_high:4, + h:1, + tsc:3; + u_int8_t hsn:6, + maio_low:2; + } h1; + struct { + u_int8_t arfcn_high:2, + spare:2, + h:1, + tsc:3; + u_int8_t arfcn_low; + } h0; + }; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.21aa */ +struct gsm48_multi_rate_conf { + u_int8_t smod : 2, + spare: 1, + icmi : 1, + nscb : 1, + ver : 3; + u_int8_t m4_75 : 1, + m5_15 : 1, + m5_90 : 1, + m6_70 : 1, + m7_40 : 1, + m7_95 : 1, + m10_2 : 1, + m12_2 : 1; +} __attribute__((packed)); + +/* Chapter 10.5.2.30 */ +struct gsm48_req_ref { + u_int8_t ra; + u_int8_t t3_high:3, + t1_:5; + u_int8_t t2:5, + t3_low:3; +} __attribute__ ((packed)); + +/* + * Chapter 9.1.5/9.1.6 + * + * For 9.1.6 the chan_desc has the meaning of 10.5.2.5a + */ +struct gsm48_chan_mode_modify { + struct gsm48_chan_desc chan_desc; + u_int8_t mode; +} __attribute__ ((packed)); + +enum gsm48_chan_mode { + GSM48_CMODE_SIGN = 0x00, + GSM48_CMODE_SPEECH_V1 = 0x01, + GSM48_CMODE_SPEECH_EFR = 0x21, + GSM48_CMODE_SPEECH_AMR = 0x41, + GSM48_CMODE_DATA_14k5 = 0x0f, + GSM48_CMODE_DATA_12k0 = 0x03, + GSM48_CMODE_DATA_6k0 = 0x0b, + GSM48_CMODE_DATA_3k6 = 0x23, +}; + +/* Chapter 9.1.2 */ +struct gsm48_ass_cmd { + /* Semantic is from 10.5.2.5a */ + struct gsm48_chan_desc chan_desc; + u_int8_t power_command; + u_int8_t data[0]; +} __attribute__((packed)); + +/* Chapter 10.5.2.2 */ +struct gsm48_cell_desc { + u_int8_t bcc:3, + ncc:3, + arfcn_hi:2; + u_int8_t arfcn_lo; +} __attribute__((packed)); + +/* Chapter 9.1.15 */ +struct gsm48_ho_cmd { + struct gsm48_cell_desc cell_desc; + struct gsm48_chan_desc chan_desc; + u_int8_t ho_ref; + u_int8_t power_command; + u_int8_t data[0]; +} __attribute__((packed)); + +/* Chapter 9.1.18 */ +struct gsm48_imm_ass { + u_int8_t l2_plen; + u_int8_t proto_discr; + u_int8_t msg_type; + u_int8_t page_mode; + struct gsm48_chan_desc chan_desc; + struct gsm48_req_ref req_ref; + u_int8_t timing_advance; + u_int8_t mob_alloc_len; + u_int8_t mob_alloc[0]; +} __attribute__ ((packed)); + +/* Chapter 10.5.1.3 */ +struct gsm48_loc_area_id { + u_int8_t digits[3]; /* BCD! */ + u_int16_t lac; +} __attribute__ ((packed)); + +/* Section 9.2.2 */ +struct gsm48_auth_req { + u_int8_t key_seq:4, + spare:4; + u_int8_t rand[16]; +} __attribute__ ((packed)); + +/* Section 9.2.15 */ +struct gsm48_loc_upd_req { + u_int8_t type:4, + key_seq:4; + struct gsm48_loc_area_id lai; + struct gsm48_classmark1 classmark1; + u_int8_t mi_len; + u_int8_t mi[0]; +} __attribute__ ((packed)); + +/* Section 10.1 */ +struct gsm48_hdr { + u_int8_t proto_discr; + u_int8_t msg_type; + u_int8_t data[0]; +} __attribute__ ((packed)); + +/* Section 9.1.3x System information Type header */ +struct gsm48_system_information_type_header { + u_int8_t l2_plen; + u_int8_t rr_protocol_discriminator :4, + skip_indicator:4; + u_int8_t system_information; +} __attribute__ ((packed)); + +struct gsm48_rach_control { + u_int8_t re :1, + cell_bar :1, + tx_integer :4, + max_trans :2; + u_int8_t t2; + u_int8_t t3; +} __attribute__ ((packed)); + +/* Section 10.5.2.4 Cell Selection Parameters */ +struct gsm48_cell_sel_par { + u_int8_t ms_txpwr_max_ccch:5, /* GSM 05.08 MS-TXPWR-MAX-CCCH */ + cell_resel_hyst:3; /* GSM 05.08 CELL-RESELECT-HYSTERESIS */ + u_int8_t rxlev_acc_min:6, /* GSM 05.08 RXLEV-ACCESS-MIN */ + neci:1, + acs:1; +} __attribute__ ((packed)); + +/* Section 10.5.2.11 Control Channel Description , Figure 10.5.33 */ +struct gsm48_control_channel_descr { + u_int8_t ccch_conf :3, + bs_ag_blks_res :3, + att :1, + spare1 :1; + u_int8_t bs_pa_mfrms : 3, + spare2 :5; + u_int8_t t3212; +} __attribute__ ((packed)); + +struct gsm48_cell_options { + u_int8_t radio_link_timeout:4, + dtx:2, + pwrc:1, + spare:1; +} __attribute__ ((packed)); + +/* Section 9.2.9 CM service request */ +struct gsm48_service_request { + u_int8_t cm_service_type : 4, + cipher_key_seq : 4; + /* length + 3 bytes */ + u_int32_t classmark; + u_int8_t mi_len; + u_int8_t mi[0]; + /* optional priority level */ +} __attribute__ ((packed)); + +/* Section 9.1.31 System information Type 1 */ +struct gsm48_system_information_type_1 { + struct gsm48_system_information_type_header header; + u_int8_t cell_channel_description[16]; + struct gsm48_rach_control rach_control; + u_int8_t rest_octets[0]; /* NCH position on the CCCH */ +} __attribute__ ((packed)); + +/* Section 9.1.32 System information Type 2 */ +struct gsm48_system_information_type_2 { + struct gsm48_system_information_type_header header; + u_int8_t bcch_frequency_list[16]; + u_int8_t ncc_permitted; + struct gsm48_rach_control rach_control; +} __attribute__ ((packed)); + +/* Section 9.1.35 System information Type 3 */ +struct gsm48_system_information_type_3 { + struct gsm48_system_information_type_header header; + u_int16_t cell_identity; + struct gsm48_loc_area_id lai; + struct gsm48_control_channel_descr control_channel_desc; + struct gsm48_cell_options cell_options; + struct gsm48_cell_sel_par cell_sel_par; + struct gsm48_rach_control rach_control; + u_int8_t rest_octets[0]; +} __attribute__ ((packed)); + +/* Section 9.1.36 System information Type 4 */ +struct gsm48_system_information_type_4 { + struct gsm48_system_information_type_header header; + struct gsm48_loc_area_id lai; + struct gsm48_cell_sel_par cell_sel_par; + struct gsm48_rach_control rach_control; + /* optional CBCH conditional CBCH... followed by + mandantory SI 4 Reset Octets + */ + u_int8_t data[0]; +} __attribute__ ((packed)); + +/* Section 9.1.37 System information Type 5 */ +struct gsm48_system_information_type_5 { + u_int8_t rr_protocol_discriminator :4, + skip_indicator:4; + u_int8_t system_information; + u_int8_t bcch_frequency_list[16]; +} __attribute__ ((packed)); + +/* Section 9.1.40 System information Type 6 */ +struct gsm48_system_information_type_6 { + u_int8_t rr_protocol_discriminator :4, + skip_indicator:4; + u_int8_t system_information; + u_int16_t cell_identity; + struct gsm48_loc_area_id lai; + struct gsm48_cell_options cell_options; + u_int8_t ncc_permitted; + u_int8_t rest_octets[0]; +} __attribute__ ((packed)); + +/* Section 9.1.43a System Information type 13 */ +struct gsm48_system_information_type_13 { + struct gsm48_system_information_type_header header; + u_int8_t rest_octets[0]; +} __attribute__ ((packed)); + +/* Section 9.2.12 IMSI Detach Indication */ +struct gsm48_imsi_detach_ind { + struct gsm48_classmark1 classmark1; + u_int8_t mi_len; + u_int8_t mi[0]; +} __attribute__ ((packed)); + +/* Section 10.2 + GSM 04.07 12.2.3.1.1 */ +#define GSM48_PDISC_GROUP_CC 0x00 +#define GSM48_PDISC_BCAST_CC 0x01 +#define GSM48_PDISC_PDSS1 0x02 +#define GSM48_PDISC_CC 0x03 +#define GSM48_PDISC_PDSS2 0x04 +#define GSM48_PDISC_MM 0x05 +#define GSM48_PDISC_RR 0x06 +#define GSM48_PDISC_MM_GPRS 0x08 +#define GSM48_PDISC_SMS 0x09 +#define GSM48_PDISC_SM_GPRS 0x0a +#define GSM48_PDISC_NC_SS 0x0b +#define GSM48_PDISC_LOC 0x0c +#define GSM48_PDISC_MASK 0x0f +#define GSM48_PDISC_USSD 0x11 + +/* Section 10.4 */ +#define GSM48_MT_RR_INIT_REQ 0x3c +#define GSM48_MT_RR_ADD_ASS 0x3b +#define GSM48_MT_RR_IMM_ASS 0x3f +#define GSM48_MT_RR_IMM_ASS_EXT 0x39 +#define GSM48_MT_RR_IMM_ASS_REJ 0x3a + +#define GSM48_MT_RR_CIPH_M_CMD 0x35 +#define GSM48_MT_RR_CIPH_M_COMPL 0x32 + +#define GSM48_MT_RR_CFG_CHG_CMD 0x30 +#define GSM48_MT_RR_CFG_CHG_ACK 0x31 +#define GSM48_MT_RR_CFG_CHG_REJ 0x33 + +#define GSM48_MT_RR_ASS_CMD 0x2e +#define GSM48_MT_RR_ASS_COMPL 0x29 +#define GSM48_MT_RR_ASS_FAIL 0x2f +#define GSM48_MT_RR_HANDO_CMD 0x2b +#define GSM48_MT_RR_HANDO_COMPL 0x2c +#define GSM48_MT_RR_HANDO_FAIL 0x28 +#define GSM48_MT_RR_HANDO_INFO 0x2d + +#define GSM48_MT_RR_CELL_CHG_ORDER 0x08 +#define GSM48_MT_RR_PDCH_ASS_CMD 0x23 + +#define GSM48_MT_RR_CHAN_REL 0x0d +#define GSM48_MT_RR_PART_REL 0x0a +#define GSM48_MT_RR_PART_REL_COMP 0x0f + +#define GSM48_MT_RR_PAG_REQ_1 0x21 +#define GSM48_MT_RR_PAG_REQ_2 0x22 +#define GSM48_MT_RR_PAG_REQ_3 0x24 +#define GSM48_MT_RR_PAG_RESP 0x27 +#define GSM48_MT_RR_NOTIF_NCH 0x20 +#define GSM48_MT_RR_NOTIF_FACCH 0x25 +#define GSM48_MT_RR_NOTIF_RESP 0x26 + +#define GSM48_MT_RR_SYSINFO_8 0x18 +#define GSM48_MT_RR_SYSINFO_1 0x19 +#define GSM48_MT_RR_SYSINFO_2 0x1a +#define GSM48_MT_RR_SYSINFO_3 0x1b +#define GSM48_MT_RR_SYSINFO_4 0x1c +#define GSM48_MT_RR_SYSINFO_5 0x1d +#define GSM48_MT_RR_SYSINFO_6 0x1e +#define GSM48_MT_RR_SYSINFO_7 0x1f + +#define GSM48_MT_RR_SYSINFO_2bis 0x02 +#define GSM48_MT_RR_SYSINFO_2ter 0x03 +#define GSM48_MT_RR_SYSINFO_5bis 0x05 +#define GSM48_MT_RR_SYSINFO_5ter 0x06 +#define GSM48_MT_RR_SYSINFO_9 0x04 +#define GSM48_MT_RR_SYSINFO_13 0x00 + +#define GSM48_MT_RR_SYSINFO_16 0x3d +#define GSM48_MT_RR_SYSINFO_17 0x3e + +#define GSM48_MT_RR_CHAN_MODE_MODIF 0x10 +#define GSM48_MT_RR_STATUS 0x12 +#define GSM48_MT_RR_CHAN_MODE_MODIF_ACK 0x17 +#define GSM48_MT_RR_FREQ_REDEF 0x14 +#define GSM48_MT_RR_MEAS_REP 0x15 +#define GSM48_MT_RR_CLSM_CHG 0x16 +#define GSM48_MT_RR_CLSM_ENQ 0x13 +#define GSM48_MT_RR_EXT_MEAS_REP 0x36 +#define GSM48_MT_RR_EXT_MEAS_REP_ORD 0x37 +#define GSM48_MT_RR_GPRS_SUSP_REQ 0x34 + +#define GSM48_MT_RR_VGCS_UPL_GRANT 0x08 +#define GSM48_MT_RR_UPLINK_RELEASE 0x0e +#define GSM48_MT_RR_UPLINK_FREE 0x0c +#define GSM48_MT_RR_UPLINK_BUSY 0x2a +#define GSM48_MT_RR_TALKER_IND 0x11 + +#define GSM48_MT_RR_APP_INFO 0x38 + +/* Table 10.2/3GPP TS 04.08 */ +#define GSM48_MT_MM_IMSI_DETACH_IND 0x01 +#define GSM48_MT_MM_LOC_UPD_ACCEPT 0x02 +#define GSM48_MT_MM_LOC_UPD_REJECT 0x04 +#define GSM48_MT_MM_LOC_UPD_REQUEST 0x08 + +#define GSM48_MT_MM_AUTH_REJ 0x11 +#define GSM48_MT_MM_AUTH_REQ 0x12 +#define GSM48_MT_MM_AUTH_RESP 0x14 +#define GSM48_MT_MM_ID_REQ 0x18 +#define GSM48_MT_MM_ID_RESP 0x19 +#define GSM48_MT_MM_TMSI_REALL_CMD 0x1a +#define GSM48_MT_MM_TMSI_REALL_COMPL 0x1b + +#define GSM48_MT_MM_CM_SERV_ACC 0x21 +#define GSM48_MT_MM_CM_SERV_REJ 0x22 +#define GSM48_MT_MM_CM_SERV_ABORT 0x23 +#define GSM48_MT_MM_CM_SERV_REQ 0x24 +#define GSM48_MT_MM_CM_SERV_PROMPT 0x25 +#define GSM48_MT_MM_CM_REEST_REQ 0x28 +#define GSM48_MT_MM_ABORT 0x29 + +#define GSM48_MT_MM_NULL 0x30 +#define GSM48_MT_MM_STATUS 0x31 +#define GSM48_MT_MM_INFO 0x32 + +/* Table 10.3/3GPP TS 04.08 */ +#define GSM48_MT_CC_ALERTING 0x01 +#define GSM48_MT_CC_CALL_CONF 0x08 +#define GSM48_MT_CC_CALL_PROC 0x02 +#define GSM48_MT_CC_CONNECT 0x07 +#define GSM48_MT_CC_CONNECT_ACK 0x0f +#define GSM48_MT_CC_EMERG_SETUP 0x0e +#define GSM48_MT_CC_PROGRESS 0x03 +#define GSM48_MT_CC_ESTAB 0x04 +#define GSM48_MT_CC_ESTAB_CONF 0x06 +#define GSM48_MT_CC_RECALL 0x0b +#define GSM48_MT_CC_START_CC 0x09 +#define GSM48_MT_CC_SETUP 0x05 + +#define GSM48_MT_CC_MODIFY 0x17 +#define GSM48_MT_CC_MODIFY_COMPL 0x1f +#define GSM48_MT_CC_MODIFY_REJECT 0x13 +#define GSM48_MT_CC_USER_INFO 0x10 +#define GSM48_MT_CC_HOLD 0x18 +#define GSM48_MT_CC_HOLD_ACK 0x19 +#define GSM48_MT_CC_HOLD_REJ 0x1a +#define GSM48_MT_CC_RETR 0x1c +#define GSM48_MT_CC_RETR_ACK 0x1d +#define GSM48_MT_CC_RETR_REJ 0x1e + +#define GSM48_MT_CC_DISCONNECT 0x25 +#define GSM48_MT_CC_RELEASE 0x2d +#define GSM48_MT_CC_RELEASE_COMPL 0x2a + +#define GSM48_MT_CC_CONG_CTRL 0x39 +#define GSM48_MT_CC_NOTIFY 0x3e +#define GSM48_MT_CC_STATUS 0x3d +#define GSM48_MT_CC_STATUS_ENQ 0x34 +#define GSM48_MT_CC_START_DTMF 0x35 +#define GSM48_MT_CC_STOP_DTMF 0x31 +#define GSM48_MT_CC_STOP_DTMF_ACK 0x32 +#define GSM48_MT_CC_START_DTMF_ACK 0x36 +#define GSM48_MT_CC_START_DTMF_REJ 0x37 +#define GSM48_MT_CC_FACILITY 0x3a + +/* FIXME: Table 10.4 / 10.4a (GPRS) */ + +/* Section 10.5.2.26, Table 10.5.64 */ +#define GSM48_PM_MASK 0x03 +#define GSM48_PM_NORMAL 0x00 +#define GSM48_PM_EXTENDED 0x01 +#define GSM48_PM_REORG 0x02 +#define GSM48_PM_SAME 0x03 + +/* Chapter 10.5.3.5 / Table 10.5.93 */ +#define GSM48_LUPD_NORMAL 0x0 +#define GSM48_LUPD_PERIODIC 0x1 +#define GSM48_LUPD_IMSI_ATT 0x2 +#define GSM48_LUPD_RESERVED 0x3 + +/* Table 10.5.4 */ +#define GSM_MI_TYPE_MASK 0x07 +#define GSM_MI_TYPE_NONE 0x00 +#define GSM_MI_TYPE_IMSI 0x01 +#define GSM_MI_TYPE_IMEI 0x02 +#define GSM_MI_TYPE_IMEISV 0x03 +#define GSM_MI_TYPE_TMSI 0x04 +#define GSM_MI_ODD 0x08 + +#define GSM48_IE_MUL_RATE_CFG 0x03 /* 10.5.2.21aa */ +#define GSM48_IE_MOBILE_ID 0x17 +#define GSM48_IE_NAME_LONG 0x43 /* 10.5.3.5a */ +#define GSM48_IE_NAME_SHORT 0x45 /* 10.5.3.5a */ +#define GSM48_IE_UTC 0x46 /* 10.5.3.8 */ +#define GSM48_IE_NET_TIME_TZ 0x47 /* 10.5.3.9 */ +#define GSM48_IE_LSA_IDENT 0x48 /* 10.5.3.11 */ + +#define GSM48_IE_BEARER_CAP 0x04 /* 10.5.4.5 */ +#define GSM48_IE_CAUSE 0x08 /* 10.5.4.11 */ +#define GSM48_IE_CC_CAP 0x15 /* 10.5.4.5a */ +#define GSM48_IE_ALERT 0x19 /* 10.5.4.26 */ +#define GSM48_IE_FACILITY 0x1c /* 10.5.4.15 */ +#define GSM48_IE_PROGR_IND 0x1e /* 10.5.4.21 */ +#define GSM48_IE_AUX_STATUS 0x24 /* 10.5.4.4 */ +#define GSM48_IE_NOTIFY 0x27 /* 10.5.4.20 */ +#define GSM48_IE_KPD_FACILITY 0x2c /* 10.5.4.17 */ +#define GSM48_IE_SIGNAL 0x34 /* 10.5.4.23 */ +#define GSM48_IE_CONN_BCD 0x4c /* 10.5.4.13 */ +#define GSM48_IE_CONN_SUB 0x4d /* 10.5.4.14 */ +#define GSM48_IE_CALLING_BCD 0x5c /* 10.5.4.9 */ +#define GSM48_IE_CALLING_SUB 0x5d /* 10.5.4.10 */ +#define GSM48_IE_CALLED_BCD 0x5e /* 10.5.4.7 */ +#define GSM48_IE_CALLED_SUB 0x6d /* 10.5.4.8 */ +#define GSM48_IE_REDIR_BCD 0x74 /* 10.5.4.21a */ +#define GSM48_IE_REDIR_SUB 0x75 /* 10.5.4.21b */ +#define GSM48_IE_LOWL_COMPAT 0x7c /* 10.5.4.18 */ +#define GSM48_IE_HIGHL_COMPAT 0x7d /* 10.5.4.16 */ +#define GSM48_IE_USER_USER 0x7e /* 10.5.4.25 */ +#define GSM48_IE_SS_VERS 0x7f /* 10.5.4.24 */ +#define GSM48_IE_MORE_DATA 0xa0 /* 10.5.4.19 */ +#define GSM48_IE_CLIR_SUPP 0xa1 /* 10.5.4.11a */ +#define GSM48_IE_CLIR_INVOC 0xa2 /* 10.5.4.11b */ +#define GSM48_IE_REV_C_SETUP 0xa3 /* 10.5.4.22a */ +#define GSM48_IE_REPEAT_CIR 0xd1 /* 10.5.4.22 */ +#define GSM48_IE_REPEAT_SEQ 0xd3 /* 10.5.4.22 */ + +/* Section 10.5.4.11 / Table 10.5.122 */ +#define GSM48_CAUSE_CS_GSM 0x60 + +/* Section 9.1.2 / Table 9.3 */ +#define GSM48_IE_FRQLIST_AFTER 0x05 +#define GSM48_IE_CELL_CH_DESC 0x62 +#define GSM48_IE_MSLOT_DESC 0x10 +#define GSM48_IE_CHANMODE_1 0x63 +#define GSM48_IE_CHANMODE_2 0x11 +#define GSM48_IE_CHANMODE_3 0x13 +#define GSM48_IE_CHANMODE_4 0x14 +#define GSM48_IE_CHANMODE_5 0x15 +#define GSM48_IE_CHANMODE_6 0x16 +#define GSM48_IE_CHANMODE_7 0x17 +#define GSM48_IE_CHANMODE_8 0x18 +#define GSM48_IE_CHANDESC_2 0x64 +/* FIXME */ + +/* Section 10.5.4.23 / Table 10.5.130 */ +enum gsm48_signal_val { + GSM48_SIGNAL_DIALTONE = 0x00, + GSM48_SIGNAL_RINGBACK = 0x01, + GSM48_SIGNAL_INTERCEPT = 0x02, + GSM48_SIGNAL_NET_CONG = 0x03, + GSM48_SIGNAL_BUSY = 0x04, + GSM48_SIGNAL_CONFIRM = 0x05, + GSM48_SIGNAL_ANSWER = 0x06, + GSM48_SIGNAL_CALL_WAIT = 0x07, + GSM48_SIGNAL_OFF_HOOK = 0x08, + GSM48_SIGNAL_OFF = 0x3f, + GSM48_SIGNAL_ALERT_OFF = 0x4f, +}; + +enum gsm48_cause_loc { + GSM48_CAUSE_LOC_USER = 0x00, + GSM48_CAUSE_LOC_PRN_S_LU = 0x01, + GSM48_CAUSE_LOC_PUN_S_LU = 0x02, + GSM48_CAUSE_LOC_TRANS_NET = 0x03, + GSM48_CAUSE_LOC_PUN_S_RU = 0x04, + GSM48_CAUSE_LOC_PRN_S_RU = 0x05, + /* not defined */ + GSM48_CAUSE_LOC_INN_NET = 0x07, + GSM48_CAUSE_LOC_NET_BEYOND = 0x0a, +}; + +/* Section 10.5.2.31 RR Cause / Table 10.5.70 */ +enum gsm48_rr_cause { + GSM48_RR_CAUSE_NORMAL = 0x00, + GSM48_RR_CAUSE_ABNORMAL_UNSPEC = 0x01, + GSM48_RR_CAUSE_ABNORMAL_UNACCT = 0x02, + GSM48_RR_CAUSE_ABNORMAL_TIMER = 0x03, + GSM48_RR_CAUSE_ABNORMAL_NOACT = 0x04, + GSM48_RR_CAUSE_PREMPTIVE_REL = 0x05, + GSM48_RR_CAUSE_HNDOVER_IMP = 0x06, + GSM48_RR_CAUSE_CHAN_MODE_UNACCT = 0x07, + GSM48_RR_CAUSE_FREQ_NOT_IMPL = 0x08, + GSM48_RR_CAUSE_CALL_CLEARED = 0x41, + GSM48_RR_CAUSE_SEMANT_INCORR = 0x5f, + GSM48_RR_CAUSE_INVALID_MAND_INF = 0x60, + GSM48_RR_CAUSE_MSG_TYPE_N = 0x61, + GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT= 0x62, + GSM48_RR_CAUSE_COND_IE_ERROR = 0x64, + GSM48_RR_CAUSE_NO_CELL_ALLOC_A = 0x65, + GSM48_RR_CAUSE_PROT_ERROR_UNSPC = 0x6f, +}; + +/* Section 10.5.4.11 CC Cause / Table 10.5.123 */ +enum gsm48_cc_cause { + GSM48_CC_CAUSE_UNASSIGNED_NR = 1, + GSM48_CC_CAUSE_NO_ROUTE = 3, + GSM48_CC_CAUSE_CHAN_UNACCEPT = 6, + GSM48_CC_CAUSE_OP_DET_BARRING = 8, + GSM48_CC_CAUSE_NORM_CALL_CLEAR = 16, + GSM48_CC_CAUSE_USER_BUSY = 17, + GSM48_CC_CAUSE_USER_NOTRESPOND = 18, + GSM48_CC_CAUSE_USER_ALERTING_NA = 19, + GSM48_CC_CAUSE_CALL_REJECTED = 21, + GSM48_CC_CAUSE_NUMBER_CHANGED = 22, + GSM48_CC_CAUSE_PRE_EMPTION = 25, + GSM48_CC_CAUSE_NONSE_USER_CLR = 26, + GSM48_CC_CAUSE_DEST_OOO = 27, + GSM48_CC_CAUSE_INV_NR_FORMAT = 28, + GSM48_CC_CAUSE_FACILITY_REJ = 29, + GSM48_CC_CAUSE_RESP_STATUS_INQ = 30, + GSM48_CC_CAUSE_NORMAL_UNSPEC = 31, + GSM48_CC_CAUSE_NO_CIRCUIT_CHAN = 34, + GSM48_CC_CAUSE_NETWORK_OOO = 38, + GSM48_CC_CAUSE_TEMP_FAILURE = 41, + GSM48_CC_CAUSE_SWITCH_CONG = 42, + GSM48_CC_CAUSE_ACC_INF_DISCARD = 43, + GSM48_CC_CAUSE_REQ_CHAN_UNAVAIL = 44, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL = 47, + GSM48_CC_CAUSE_QOS_UNAVAIL = 49, + GSM48_CC_CAUSE_REQ_FAC_NOT_SUBSC= 50, + GSM48_CC_CAUSE_INC_BARRED_CUG = 55, + GSM48_CC_CAUSE_BEARER_CAP_UNAUTH= 57, + GSM48_CC_CAUSE_BEARER_CA_UNAVAIL= 58, + GSM48_CC_CAUSE_SERV_OPT_UNAVAIL = 63, + GSM48_CC_CAUSE_BEARERSERV_UNIMPL= 65, + GSM48_CC_CAUSE_ACM_GE_ACM_MAX = 68, + GSM48_CC_CAUSE_REQ_FAC_NOTIMPL = 69, + GSM48_CC_CAUSE_RESTR_BCAP_AVAIL = 70, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL = 79, + GSM48_CC_CAUSE_INVAL_TRANS_ID = 81, + GSM48_CC_CAUSE_USER_NOT_IN_CUG = 87, + GSM48_CC_CAUSE_INCOMPAT_DEST = 88, + GSM48_CC_CAUSE_INVAL_TRANS_NET = 91, + GSM48_CC_CAUSE_SEMANTIC_INCORR = 95, + GSM48_CC_CAUSE_INVAL_MAND_INF = 96, + GSM48_CC_CAUSE_MSGTYPE_NOTEXIST = 97, + GSM48_CC_CAUSE_MSGTYPE_INCOMPAT = 98, + GSM48_CC_CAUSE_IE_NOTEXIST = 99, + GSM48_CC_CAUSE_COND_IE_ERR = 100, + GSM48_CC_CAUSE_MSG_INCOMP_STATE = 101, + GSM48_CC_CAUSE_RECOVERY_TIMER = 102, + GSM48_CC_CAUSE_PROTO_ERR = 111, + GSM48_CC_CAUSE_INTERWORKING = 127, +}; + +/* Annex G, GSM specific cause values for mobility management */ +enum gsm48_reject_value { + GSM48_REJECT_IMSI_UNKNOWN_IN_HLR = 2, + GSM48_REJECT_ILLEGAL_MS = 3, + GSM48_REJECT_IMSI_UNKNOWN_IN_VLR = 4, + GSM48_REJECT_IMEI_NOT_ACCEPTED = 5, + GSM48_REJECT_ILLEGAL_ME = 6, + GSM48_REJECT_PLMN_NOT_ALLOWED = 11, + GSM48_REJECT_LOC_NOT_ALLOWED = 12, + GSM48_REJECT_ROAMING_NOT_ALLOWED = 13, + GSM48_REJECT_NETWORK_FAILURE = 17, + GSM48_REJECT_CONGESTION = 22, + GSM48_REJECT_SRV_OPT_NOT_SUPPORTED = 32, + GSM48_REJECT_RQD_SRV_OPT_NOT_SUPPORTED = 33, + GSM48_REJECT_SRV_OPT_TMP_OUT_OF_ORDER = 34, + GSM48_REJECT_CALL_CAN_NOT_BE_IDENTIFIED = 38, + GSM48_REJECT_INCORRECT_MESSAGE = 95, + GSM48_REJECT_INVALID_MANDANTORY_INF = 96, + GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED = 97, + GSM48_REJECT_MSG_TYPE_NOT_COMPATIBLE = 98, + GSM48_REJECT_INF_ELEME_NOT_IMPLEMENTED = 99, + GSM48_REJECT_CONDTIONAL_IE_ERROR = 100, + GSM48_REJECT_MSG_NOT_COMPATIBLE = 101, + GSM48_REJECT_PROTOCOL_ERROR = 111, + + /* according to G.6 Additional cause codes for GMM */ + GSM48_REJECT_GPRS_NOT_ALLOWED = 7, + GSM48_REJECT_SERVICES_NOT_ALLOWED = 8, + GSM48_REJECT_MS_IDENTITY_NOT_DERVIVABLE = 9, + GSM48_REJECT_IMPLICITLY_DETACHED = 10, + GSM48_REJECT_GPRS_NOT_ALLOWED_IN_PLMN = 14, + GSM48_REJECT_MSC_TMP_NOT_REACHABLE = 16, +}; + +enum chreq_type { + CHREQ_T_EMERG_CALL, + CHREQ_T_CALL_REEST_TCH_F, + CHREQ_T_CALL_REEST_TCH_H, + CHREQ_T_CALL_REEST_TCH_H_DBL, + CHREQ_T_SDCCH, + CHREQ_T_TCH_F, + CHREQ_T_VOICE_CALL_TCH_H, + CHREQ_T_DATA_CALL_TCH_H, + CHREQ_T_LOCATION_UPD, + CHREQ_T_PAG_R_ANY_NECI0, + CHREQ_T_PAG_R_ANY_NECI1, + CHREQ_T_PAG_R_TCH_F, + CHREQ_T_PAG_R_TCH_FH, + CHREQ_T_LMU, + CHREQ_T_RESERVED_SDCCH, + CHREQ_T_RESERVED_IGNORE, +}; + +/* Chapter 11.3 */ +#define GSM48_T301 180, 0 +#define GSM48_T303 30, 0 +#define GSM48_T305 30, 0 +#define GSM48_T306 30, 0 +#define GSM48_T308 10, 0 +#define GSM48_T310 180, 0 +#define GSM48_T313 30, 0 +#define GSM48_T323 30, 0 +#define GSM48_T331 30, 0 +#define GSM48_T333 30, 0 +#define GSM48_T334 25, 0 /* min 15 */ +#define GSM48_T338 30, 0 + +/* Chapter 5.1.2.2 */ +#define GSM_CSTATE_NULL 0 +#define GSM_CSTATE_INITIATED 1 +#define GSM_CSTATE_MO_CALL_PROC 3 +#define GSM_CSTATE_CALL_DELIVERED 4 +#define GSM_CSTATE_CALL_PRESENT 6 +#define GSM_CSTATE_CALL_RECEIVED 7 +#define GSM_CSTATE_CONNECT_REQUEST 8 +#define GSM_CSTATE_MO_TERM_CALL_CONF 9 +#define GSM_CSTATE_ACTIVE 10 +#define GSM_CSTATE_DISCONNECT_REQ 12 +#define GSM_CSTATE_DISCONNECT_IND 12 +#define GSM_CSTATE_RELEASE_REQ 19 +#define GSM_CSTATE_MO_ORIG_MODIFY 26 +#define GSM_CSTATE_MO_TERM_MODIFY 27 +#define GSM_CSTATE_CONNECT_IND 28 + +#define SBIT(a) (1 << a) +#define ALL_STATES 0xffffffff + +/* Table 10.5.3/3GPP TS 04.08: Location Area Identification information element */ +#define GSM_LAC_RESERVED_DETACHED 0x0 +#define GSM_LAC_RESERVED_ALL_BTS 0xfffe + +/* GSM 04.08 Bearer Capability: Information Transfer Capability */ +enum gsm48_bcap_itcap { + GSM48_BCAP_ITCAP_SPEECH = 0, + GSM48_BCAP_ITCAP_UNR_DIG_INF = 1, + GSM48_BCAP_ITCAP_3k1_AUDIO = 2, + GSM48_BCAP_ITCAP_FAX_G3 = 3, + GSM48_BCAP_ITCAP_OTHER = 5, + GSM48_BCAP_ITCAP_RESERVED = 7, +}; + +/* GSM 04.08 Bearer Capability: Transfer Mode */ +enum gsm48_bcap_tmod { + GSM48_BCAP_TMOD_CIRCUIT = 0, + GSM48_BCAP_TMOD_PACKET = 1, +}; + +/* GSM 04.08 Bearer Capability: Coding Standard */ +enum gsm48_bcap_coding { + GSM48_BCAP_CODING_GSM_STD = 0, +}; + +/* GSM 04.08 Bearer Capability: Radio Channel Requirements */ +enum gsm48_bcap_rrq { + GSM48_BCAP_RRQ_FR_ONLY = 1, + GSM48_BCAP_RRQ_DUAL_HR = 2, + GSM48_BCAP_RRQ_DUAL_FR = 3, +}; + + +#define GSM48_TMSI_LEN 5 +#define GSM48_MID_TMSI_LEN (GSM48_TMSI_LEN + 2) +#define GSM48_MI_SIZE 32 + + +struct msgb; +struct gsm_bts; +struct gsm_subscriber; +struct gsm_network; +struct gsm_trans; + +/* config options controlling the behaviour of the lower leves */ +void gsm0408_allow_everyone(int allow); + +int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id); +void gsm0408_generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc, + u_int16_t mnc, u_int16_t lac); +enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci); +enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci); + +int gsm48_tx_mm_info(struct gsm_lchan *lchan); +int gsm48_tx_mm_auth_req(struct gsm_lchan *lchan, u_int8_t *rand, int key_seq); +int gsm48_tx_mm_auth_rej(struct gsm_lchan *lchan); +struct msgb *gsm48_msgb_alloc(void); +int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans); +int gsm48_generate_mid_from_tmsi(u_int8_t *buf, u_int32_t tmsi); +int gsm48_generate_mid_from_imsi(u_int8_t *buf, const char* imsi); +int gsm48_mi_to_string(char *string, const int str_len, const u_int8_t *mi, const int mi_len); + +int gsm48_send_rr_release(struct gsm_lchan *lchan); +int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv); +int gsm48_send_rr_app_info(struct gsm_lchan *lchan, u_int8_t apdu_id, + u_int8_t apdu_len, const u_int8_t *apdu); +int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, u_int8_t power_class); +int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan, + u_int8_t power_command, u_int8_t ho_ref); + +int bsc_upqueue(struct gsm_network *net); + +int mncc_send(struct gsm_network *net, int msg_type, void *arg); + +/* convert a ASCII phone number to call-control BCD */ +int encode_bcd_number(u_int8_t *bcd_lv, u_int8_t max_len, + int h_len, const char *input); +int decode_bcd_number(char *output, int output_len, const u_int8_t *bcd_lv, + int h_len); + +extern const char *gsm0408_cc_msg_names[]; + +int send_siemens_mrpci(struct gsm_lchan *lchan, u_int8_t *classmark2_lv); +int gsm48_paging_extract_mi(struct msgb *msg, char *mi_string, u_int8_t *mi_type); +int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr); + +int gsm48_lchan_modify(struct gsm_lchan *lchan, u_int8_t lchan_mode); +int gsm48_rx_rr_modif_ack(struct msgb *msg); +int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg); + + +#endif diff --git a/src/host/libosmocom/include/osmocom/gsmtap.h b/src/host/libosmocom/include/osmocom/gsmtap.h new file mode 100644 index 00000000..316e73d1 --- /dev/null +++ b/src/host/libosmocom/include/osmocom/gsmtap.h @@ -0,0 +1,60 @@ +#ifndef _GSMTAP_H +#define _GSMTAP_H + +/* gsmtap header, pseudo-header in front of the actua GSM payload*/ + +#include <sys/types.h> + +#define GSMTAP_VERSION 0x01 + +#define GSMTAP_TYPE_UM 0x01 +#define GSMTAP_TYPE_ABIS 0x02 +#define GSMTAP_TYPE_UM_BURST 0x03 /* raw burst bits */ + +#define GSMTAP_BURST_UNKNOWN 0x00 +#define GSMTAP_BURST_FCCH 0x01 +#define GSMTAP_BURST_PARTIAL_SCH 0x02 +#define GSMTAP_BURST_SCH 0x03 +#define GSMTAP_BURST_CTS_SCH 0x04 +#define GSMTAP_BURST_COMPACT_SCH 0x05 +#define GSMTAP_BURST_NORMAL 0x06 +#define GSMTAP_BURST_DUMMY 0x07 +#define GSMTAP_BURST_ACCESS 0x08 +#define GSMTAP_BURST_NONE 0x09 + +struct gsmtap_hdr { + u_int8_t version; /* version, set to 0x01 currently */ + u_int8_t hdr_len; /* length in number of 32bit words */ + u_int8_t type; /* see GSMTAP_TYPE_* */ + u_int8_t timeslot; /* timeslot (0..7 on Um) */ + + u_int16_t arfcn; /* ARFCN (frequency) */ + u_int8_t noise_db; /* noise figure in dB */ + u_int8_t signal_db; /* signal level in dB */ + + u_int32_t frame_number; /* GSM Frame Number (FN) */ + + u_int8_t burst_type; /* Type of burst, see above */ + u_int8_t antenna_nr; /* Antenna Number */ + u_int16_t res; /* reserved for future use (RFU) */ + +} __attribute__((packed)); + + +/* PCAP related definitions */ +#define TCPDUMP_MAGIC 0xa1b2c3d4 +#ifndef LINKTYPE_GSMTAP +#define LINKTYPE_GSMTAP 2342 +#endif +struct pcap_timeval { + int32_t tv_sec; + int32_t tv_usec; +}; + +struct pcap_sf_pkthdr { + struct pcap_timeval ts; /* time stamp */ + u_int32_t caplen; /* lenght of portion present */ + u_int32_t len; /* length of this packet */ +}; + +#endif /* _GSMTAP_H */ diff --git a/src/host/libosmocom/include/osmocom/linuxlist.h b/src/host/libosmocom/include/osmocom/linuxlist.h new file mode 100644 index 00000000..6b77a31d --- /dev/null +++ b/src/host/libosmocom/include/osmocom/linuxlist.h @@ -0,0 +1,360 @@ +#ifndef _LINUX_LLIST_H +#define _LINUX_LLIST_H + +#include <stddef.h> + +#ifndef inline +#define inline __inline__ +#endif + +static inline void prefetch(const void *x) {;} + +/** + * container_of - cast a member of a structure out to the containing structure + * + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (typeof( ((type *)0)->member ) *)(ptr); \ + (type *)( (char *)__mptr - offsetof(type, member) );}) + + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized llist entries. + */ +#define LLIST_POISON1 ((void *) 0x00100100) +#define LLIST_POISON2 ((void *) 0x00200200) + +/* + * Simple doubly linked llist implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole llists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct llist_head { + struct llist_head *next, *prev; +}; + +#define LLIST_HEAD_INIT(name) { &(name), &(name) } + +#define LLIST_HEAD(name) \ + struct llist_head name = LLIST_HEAD_INIT(name) + +#define INIT_LLIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal llist manipulation where we know + * the prev/next entries already! + */ +static inline void __llist_add(struct llist_head *_new, + struct llist_head *prev, + struct llist_head *next) +{ + next->prev = _new; + _new->next = next; + _new->prev = prev; + prev->next = _new; +} + +/** + * llist_add - add a new entry + * @new: new entry to be added + * @head: llist head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void llist_add(struct llist_head *_new, struct llist_head *head) +{ + __llist_add(_new, head, head->next); +} + +/** + * llist_add_tail - add a new entry + * @new: new entry to be added + * @head: llist head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void llist_add_tail(struct llist_head *_new, struct llist_head *head) +{ + __llist_add(_new, head->prev, head); +} + +/* + * Delete a llist entry by making the prev/next entries + * point to each other. + * + * This is only for internal llist manipulation where we know + * the prev/next entries already! + */ +static inline void __llist_del(struct llist_head * prev, struct llist_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * llist_del - deletes entry from llist. + * @entry: the element to delete from the llist. + * Note: llist_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void llist_del(struct llist_head *entry) +{ + __llist_del(entry->prev, entry->next); + entry->next = (struct llist_head *)LLIST_POISON1; + entry->prev = (struct llist_head *)LLIST_POISON2; +} + +/** + * llist_del_init - deletes entry from llist and reinitialize it. + * @entry: the element to delete from the llist. + */ +static inline void llist_del_init(struct llist_head *entry) +{ + __llist_del(entry->prev, entry->next); + INIT_LLIST_HEAD(entry); +} + +/** + * llist_move - delete from one llist and add as another's head + * @llist: the entry to move + * @head: the head that will precede our entry + */ +static inline void llist_move(struct llist_head *llist, struct llist_head *head) +{ + __llist_del(llist->prev, llist->next); + llist_add(llist, head); +} + +/** + * llist_move_tail - delete from one llist and add as another's tail + * @llist: the entry to move + * @head: the head that will follow our entry + */ +static inline void llist_move_tail(struct llist_head *llist, + struct llist_head *head) +{ + __llist_del(llist->prev, llist->next); + llist_add_tail(llist, head); +} + +/** + * llist_empty - tests whether a llist is empty + * @head: the llist to test. + */ +static inline int llist_empty(const struct llist_head *head) +{ + return head->next == head; +} + +static inline void __llist_splice(struct llist_head *llist, + struct llist_head *head) +{ + struct llist_head *first = llist->next; + struct llist_head *last = llist->prev; + struct llist_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * llist_splice - join two llists + * @llist: the new llist to add. + * @head: the place to add it in the first llist. + */ +static inline void llist_splice(struct llist_head *llist, struct llist_head *head) +{ + if (!llist_empty(llist)) + __llist_splice(llist, head); +} + +/** + * llist_splice_init - join two llists and reinitialise the emptied llist. + * @llist: the new llist to add. + * @head: the place to add it in the first llist. + * + * The llist at @llist is reinitialised + */ +static inline void llist_splice_init(struct llist_head *llist, + struct llist_head *head) +{ + if (!llist_empty(llist)) { + __llist_splice(llist, head); + INIT_LLIST_HEAD(llist); + } +} + +/** + * llist_entry - get the struct for this entry + * @ptr: the &struct llist_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the llist_struct within the struct. + */ +#define llist_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * llist_for_each - iterate over a llist + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each(pos, head) \ + for (pos = (head)->next, prefetch(pos->next); pos != (head); \ + pos = pos->next, prefetch(pos->next)) + +/** + * __llist_for_each - iterate over a llist + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + * + * This variant differs from llist_for_each() in that it's the + * simplest possible llist iteration code, no prefetching is done. + * Use this for code that knows the llist to be very short (empty + * or 1 entry) most of the time. + */ +#define __llist_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * llist_for_each_prev - iterate over a llist backwards + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each_prev(pos, head) \ + for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \ + pos = pos->prev, prefetch(pos->prev)) + +/** + * llist_for_each_safe - iterate over a llist safe against removal of llist entry + * @pos: the &struct llist_head to use as a loop counter. + * @n: another &struct llist_head to use as temporary storage + * @head: the head for your llist. + */ +#define llist_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * llist_for_each_entry - iterate over llist of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry(pos, head, member) \ + for (pos = llist_entry((head)->next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next)) + +/** + * llist_for_each_entry_reverse - iterate backwards over llist of given type. + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_reverse(pos, head, member) \ + for (pos = llist_entry((head)->prev, typeof(*pos), member), \ + prefetch(pos->member.prev); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.prev, typeof(*pos), member), \ + prefetch(pos->member.prev)) + +/** + * llist_for_each_entry_continue - iterate over llist of given type + * continuing after existing point + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_continue(pos, head, member) \ + for (pos = llist_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next)) + +/** + * llist_for_each_entry_safe - iterate over llist of given type safe against removal of llist entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_safe(pos, n, head, member) \ + for (pos = llist_entry((head)->next, typeof(*pos), member), \ + n = llist_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = llist_entry(n->member.next, typeof(*n), member)) + +/** + * llist_for_each_rcu - iterate over an rcu-protected llist + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each_rcu(pos, head) \ + for (pos = (head)->next, prefetch(pos->next); pos != (head); \ + pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next)) + +#define __llist_for_each_rcu(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next, ({ smp_read_barrier_depends(); 0;})) + +/** + * llist_for_each_safe_rcu - iterate over an rcu-protected llist safe + * against removal of llist entry + * @pos: the &struct llist_head to use as a loop counter. + * @n: another &struct llist_head to use as temporary storage + * @head: the head for your llist. + */ +#define llist_for_each_safe_rcu(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next) + +/** + * llist_for_each_entry_rcu - iterate over rcu llist of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_rcu(pos, head, member) \ + for (pos = llist_entry((head)->next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.next, typeof(*pos), member), \ + ({ smp_read_barrier_depends(); 0;}), \ + prefetch(pos->member.next)) + + +/** + * llist_for_each_continue_rcu - iterate over an rcu-protected llist + * continuing after existing point. + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each_continue_rcu(pos, head) \ + for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \ + (pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next)) + + +#endif diff --git a/src/host/libosmocom/include/osmocom/meas_rep.h b/src/host/libosmocom/include/osmocom/meas_rep.h new file mode 100644 index 00000000..3c2c8d1c --- /dev/null +++ b/src/host/libosmocom/include/osmocom/meas_rep.h @@ -0,0 +1,85 @@ +#ifndef _MEAS_REP_H +#define _MEAS_REP_H + +#define MRC_F_PROCESSED 0x0001 + +/* extracted from a L3 measurement report IE */ +struct gsm_meas_rep_cell { + u_int8_t rxlev; + u_int8_t bsic; + u_int8_t neigh_idx; + u_int16_t arfcn; + unsigned int flags; +}; + +/* RX Level and RX Quality */ +struct gsm_rx_lev_qual { + u_int8_t rx_lev; + u_int8_t rx_qual; +}; + +/* unidirectional measumrement report */ +struct gsm_meas_rep_unidir { + struct gsm_rx_lev_qual full; + struct gsm_rx_lev_qual sub; +}; + +#define MEAS_REP_F_UL_DTX 0x01 +#define MEAS_REP_F_DL_VALID 0x02 +#define MEAS_REP_F_BA1 0x04 +#define MEAS_REP_F_DL_DTX 0x08 +#define MEAS_REP_F_MS_TO 0x10 +#define MEAS_REP_F_MS_L1 0x20 +#define MEAS_REP_F_FPC 0x40 + +/* parsed uplink and downlink measurement result */ +struct gsm_meas_rep { + /* back-pointer to the logical channel */ + struct gsm_lchan *lchan; + + /* number of the measurement report */ + u_int8_t nr; + /* flags, see MEAS_REP_F_* */ + unsigned int flags; + + /* uplink and downlink rxlev, rxqual; full and sub */ + struct gsm_meas_rep_unidir ul; + struct gsm_meas_rep_unidir dl; + + u_int8_t bs_power; + u_int8_t ms_timing_offset; + struct { + int8_t pwr; /* MS power in dBm */ + u_int8_t ta; /* MS timing advance */ + } ms_l1; + + /* neighbor measurement reports for up to 6 cells */ + int num_cell; + struct gsm_meas_rep_cell cell[6]; +}; + +enum meas_rep_field { + MEAS_REP_DL_RXLEV_FULL, + MEAS_REP_DL_RXLEV_SUB, + MEAS_REP_DL_RXQUAL_FULL, + MEAS_REP_DL_RXQUAL_SUB, + MEAS_REP_UL_RXLEV_FULL, + MEAS_REP_UL_RXLEV_SUB, + MEAS_REP_UL_RXQUAL_FULL, + MEAS_REP_UL_RXQUAL_SUB, +}; + +/* obtain an average over the last 'num' fields in the meas reps */ +int get_meas_rep_avg(const struct gsm_lchan *lchan, + enum meas_rep_field field, unsigned int num); + +/* Check if N out of M last values for FIELD are >= bd */ +int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan, + enum meas_rep_field field, + unsigned int n, unsigned int m, int be); + +unsigned int calc_initial_idx(unsigned int array_size, + unsigned int meas_rep_idx, + unsigned int num_values); + +#endif /* _MEAS_REP_H */ diff --git a/src/host/libosmocom/include/osmocom/msgb.h b/src/host/libosmocom/include/osmocom/msgb.h new file mode 100644 index 00000000..d0efc90b --- /dev/null +++ b/src/host/libosmocom/include/osmocom/msgb.h @@ -0,0 +1,114 @@ +#ifndef _MSGB_H +#define _MSGB_H + +/* (C) 2008 by Harald Welte <laforge@gnumonks.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <sys/types.h> +#include "linuxlist.h" + +struct bts_link; + +struct msgb { + struct llist_head list; + + /* ptr to the physical E1 link to the BTS(s) */ + struct gsm_bts_link *bts_link; + + /* Part of which TRX logical channel we were received / transmitted */ + struct gsm_bts_trx *trx; + struct gsm_lchan *lchan; + + unsigned char *l2h; + unsigned char *l3h; + unsigned char *smsh; + + u_int16_t data_len; + u_int16_t len; + + unsigned char *head; + unsigned char *tail; + unsigned char *data; + unsigned char _data[0]; +}; + +extern struct msgb *msgb_alloc(u_int16_t size, const char *name); +extern void msgb_free(struct msgb *m); +extern void msgb_enqueue(struct llist_head *queue, struct msgb *msg); +extern struct msgb *msgb_dequeue(struct llist_head *queue); +extern void msgb_reset(struct msgb *m); + +#define msgb_l2(m) ((void *)(m->l2h)) +#define msgb_l3(m) ((void *)(m->l3h)) +#define msgb_sms(m) ((void *)(m->smsh)) + +static inline unsigned int msgb_l2len(const struct msgb *msgb) +{ + return msgb->tail - (u_int8_t *)msgb_l2(msgb); +} + +static inline unsigned int msgb_l3len(const struct msgb *msgb) +{ + return msgb->tail - (u_int8_t *)msgb_l3(msgb); +} + +static inline unsigned int msgb_headlen(const struct msgb *msgb) +{ + return msgb->len - msgb->data_len; +} +static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len) +{ + unsigned char *tmp = msgb->tail; + msgb->tail += len; + msgb->len += len; + return tmp; +} +static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len) +{ + msgb->data -= len; + msgb->len += len; + return msgb->data; +} +static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len) +{ + msgb->len -= len; + return msgb->data += len; +} +static inline int msgb_tailroom(const struct msgb *msgb) +{ + return (msgb->data + msgb->data_len) - msgb->tail; +} + +/* increase the headroom of an empty msgb, reducing the tailroom */ +static inline void msgb_reserve(struct msgb *msg, int len) +{ + msg->data += len; + msg->tail += len; +} + +static inline struct msgb *msgb_alloc_headroom(int size, int headroom, + const char *name) +{ + struct msgb *msg = msgb_alloc(size, name); + if (msg) + msgb_reserve(msg, headroom); + return msg; +} + +#endif /* _MSGB_H */ diff --git a/src/host/libosmocom/include/osmocom/select.h b/src/host/libosmocom/include/osmocom/select.h new file mode 100644 index 00000000..2d8b3ec0 --- /dev/null +++ b/src/host/libosmocom/include/osmocom/select.h @@ -0,0 +1,22 @@ +#ifndef _BSC_SELECT_H +#define _BSC_SELECT_H + +#include "linuxlist.h" + +#define BSC_FD_READ 0x0001 +#define BSC_FD_WRITE 0x0002 +#define BSC_FD_EXCEPT 0x0004 + +struct bsc_fd { + struct llist_head list; + int fd; + unsigned int when; + int (*cb)(struct bsc_fd *fd, unsigned int what); + void *data; + unsigned int priv_nr; +}; + +int bsc_register_fd(struct bsc_fd *fd); +void bsc_unregister_fd(struct bsc_fd *fd); +int bsc_select_main(int polling); +#endif /* _BSC_SELECT_H */ diff --git a/src/host/libosmocom/include/osmocom/talloc.h b/src/host/libosmocom/include/osmocom/talloc.h new file mode 100644 index 00000000..1c243b80 --- /dev/null +++ b/src/host/libosmocom/include/osmocom/talloc.h @@ -0,0 +1,192 @@ +#ifndef _TALLOC_H_ +#define _TALLOC_H_ +/* + Unix SMB/CIFS implementation. + Samba temporary memory allocation functions + + Copyright (C) Andrew Tridgell 2004-2005 + Copyright (C) Stefan Metzmacher 2006 + + ** NOTE! The following LGPL license applies to the talloc + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> + +#define HAVE_VA_COPY + +/* this is only needed for compatibility with the old talloc */ +typedef void TALLOC_CTX; + +/* + this uses a little trick to allow __LINE__ to be stringified +*/ +#ifndef __location__ +#define __TALLOC_STRING_LINE1__(s) #s +#define __TALLOC_STRING_LINE2__(s) __TALLOC_STRING_LINE1__(s) +#define __TALLOC_STRING_LINE3__ __TALLOC_STRING_LINE2__(__LINE__) +#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__ +#endif + +#ifndef TALLOC_DEPRECATED +#define TALLOC_DEPRECATED 0 +#endif + +#ifndef PRINTF_ATTRIBUTE +#if (__GNUC__ >= 3) +/** Use gcc attribute to check printf fns. a1 is the 1-based index of + * the parameter containing the format, and a2 the index of the first + * argument. Note that some gcc 2.x versions don't handle this + * properly **/ +#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) +#else +#define PRINTF_ATTRIBUTE(a1, a2) +#endif +#endif + +/* try to make talloc_set_destructor() and talloc_steal() type safe, + if we have a recent gcc */ +#if (__GNUC__ >= 3) +#define _TALLOC_TYPEOF(ptr) __typeof__(ptr) +#define talloc_set_destructor(ptr, function) \ + do { \ + int (*_talloc_destructor_fn)(_TALLOC_TYPEOF(ptr)) = (function); \ + _talloc_set_destructor((ptr), (int (*)(void *))_talloc_destructor_fn); \ + } while(0) +/* this extremely strange macro is to avoid some braindamaged warning + stupidity in gcc 4.1.x */ +#define talloc_steal(ctx, ptr) ({ _TALLOC_TYPEOF(ptr) __talloc_steal_ret = (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)); __talloc_steal_ret; }) +#else +#define talloc_set_destructor(ptr, function) \ + _talloc_set_destructor((ptr), (int (*)(void *))(function)) +#define _TALLOC_TYPEOF(ptr) void * +#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)) +#endif + +#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr)) +#define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr)) + +/* useful macros for creating type checked pointers */ +#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type) +#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__) +#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr))) + +#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__) + +#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type) +#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__) + +#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type) +#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type) +#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__) +#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count) +#define talloc_array_length(ctx) (talloc_get_size(ctx)/sizeof(*ctx)) + +#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type) +#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__) + +#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__) + +#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type) +#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type) +#define talloc_get_type_abort(ptr, type) (type *)_talloc_get_type_abort(ptr, #type, __location__) + +#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type) + +#if TALLOC_DEPRECATED +#define talloc_zero_p(ctx, type) talloc_zero(ctx, type) +#define talloc_p(ctx, type) talloc(ctx, type) +#define talloc_array_p(ctx, type, count) talloc_array(ctx, type, count) +#define talloc_realloc_p(ctx, p, type, count) talloc_realloc(ctx, p, type, count) +#define talloc_destroy(ctx) talloc_free(ctx) +#define talloc_append_string(c, s, a) (s?talloc_strdup_append(s,a):talloc_strdup(c, a)) +#endif + +#define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx=NULL; } while(0) + +/* The following definitions come from talloc.c */ +void *_talloc(const void *context, size_t size); +void *talloc_pool(const void *context, size_t size); +void _talloc_set_destructor(const void *ptr, int (*_destructor)(void *)); +int talloc_increase_ref_count(const void *ptr); +size_t talloc_reference_count(const void *ptr); +void *_talloc_reference(const void *context, const void *ptr); +int talloc_unlink(const void *context, void *ptr); +const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +void talloc_set_name_const(const void *ptr, const char *name); +void *talloc_named(const void *context, size_t size, + const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); +void *talloc_named_const(const void *context, size_t size, const char *name); +const char *talloc_get_name(const void *ptr); +void *talloc_check_name(const void *ptr, const char *name); +void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location); +void *talloc_parent(const void *ptr); +const char *talloc_parent_name(const void *ptr); +void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); +int talloc_free(void *ptr); +void talloc_free_children(void *ptr); +void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name); +void *_talloc_steal(const void *new_ctx, const void *ptr); +void *_talloc_move(const void *new_ctx, const void *pptr); +size_t talloc_total_size(const void *ptr); +size_t talloc_total_blocks(const void *ptr); +void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, + void (*callback)(const void *ptr, + int depth, int max_depth, + int is_ref, + void *private_data), + void *private_data); +void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f); +void talloc_report_full(const void *ptr, FILE *f); +void talloc_report(const void *ptr, FILE *f); +void talloc_enable_null_tracking(void); +void talloc_disable_null_tracking(void); +void talloc_enable_leak_report(void); +void talloc_enable_leak_report_full(void); +void *_talloc_zero(const void *ctx, size_t size, const char *name); +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name); +void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name); +void *talloc_realloc_fn(const void *context, void *ptr, size_t size); +void *talloc_autofree_context(void); +size_t talloc_get_size(const void *ctx); +void *talloc_find_parent_byname(const void *ctx, const char *name); +void talloc_show_parents(const void *context, FILE *file); +int talloc_is_parent(const void *context, const void *ptr); + +char *talloc_strdup(const void *t, const char *p); +char *talloc_strdup_append(char *s, const char *a); +char *talloc_strdup_append_buffer(char *s, const char *a); + +char *talloc_strndup(const void *t, const char *p, size_t n); +char *talloc_strndup_append(char *s, const char *a, size_t n); +char *talloc_strndup_append_buffer(char *s, const char *a, size_t n); + +char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); +char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); +char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); + +void talloc_set_abort_fn(void (*abort_fn)(const char *reason)); + +#endif diff --git a/src/host/libosmocom/include/osmocom/timer.h b/src/host/libosmocom/include/osmocom/timer.h new file mode 100644 index 00000000..fee888bf --- /dev/null +++ b/src/host/libosmocom/include/osmocom/timer.h @@ -0,0 +1,72 @@ +/* + * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef TIMER_H +#define TIMER_H + +#include <sys/time.h> + +#include "linuxlist.h" + +/** + * Timer management: + * - Create a struct timer_list + * - Fill out timeout and use add_timer or + * use schedule_timer to schedule a timer in + * x seconds and microseconds from now... + * - Use del_timer to remove the timer + * + * Internally: + * - We hook into select.c to give a timeval of the + * nearest timer. On already passed timers we give + * it a 0 to immediately fire after the select + * - update_timers will call the callbacks and remove + * the timers. + * + */ +struct timer_list { + struct llist_head entry; + struct timeval timeout; + unsigned int active : 1; + unsigned int handled : 1; + unsigned int in_list : 1; + + void (*cb)(void*); + void *data; +}; + +/** + * timer management + */ +void bsc_add_timer(struct timer_list *timer); +void bsc_schedule_timer(struct timer_list *timer, int seconds, int microseconds); +void bsc_del_timer(struct timer_list *timer); +int bsc_timer_pending(struct timer_list *timer); + + +/** + * internal timer list management + */ +struct timeval *bsc_nearest_timer(); +void bsc_prepare_timers(); +int bsc_update_timers(); +int bsc_timer_check(void); + +#endif diff --git a/src/host/libosmocom/src/debug.c b/src/host/libosmocom/src/debug.c new file mode 100644 index 00000000..5dc2b225 --- /dev/null +++ b/src/host/libosmocom/src/debug.c @@ -0,0 +1,463 @@ +/* Debugging/Logging support code */ +/* (C) 2008 by Harald Welte <laforge@gnumonks.org> + * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <time.h> +#include <errno.h> + +#include <osmocom/debug.h> +#include <osmocom/talloc.h> + +struct value_string { + unsigned int value; + const char *str; +}; + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +struct gsm_bts; +struct gsm_subscriber; +struct gsm_lchan; + +/* default categories */ +static struct debug_category default_categories[Debug_LastEntry] = { + [DRLL] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DCC] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DNM] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DRR] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DRSL] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DMM] = { .enabled = 1, .loglevel = LOGL_INFO }, + [DMNCC] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DSMS] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DPAG] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DMEAS] = { .enabled = 0, .loglevel = LOGL_NOTICE }, + [DMI] = { .enabled = 0, .loglevel = LOGL_NOTICE }, + [DMIB] = { .enabled = 0, .loglevel = LOGL_NOTICE }, + [DMUX] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DINP] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DSCCP] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DMSC] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DMGCP] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DHO] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DDB] = { .enabled = 1, .loglevel = LOGL_NOTICE }, + [DREF] = { .enabled = 0, .loglevel = LOGL_NOTICE }, +}; + +const char *get_value_string(const struct value_string *vs, u_int32_t val) +{ + int i; + + for (i = 0;; i++) { + if (vs[i].value == 0 && vs[i].str == NULL) + break; + if (vs[i].value == val) + return vs[i].str; + } + return "unknown"; +} + +int get_string_value(const struct value_string *vs, const char *str) +{ + int i; + + for (i = 0;; i++) { + if (vs[i].value == 0 && vs[i].str == NULL) + break; + if (!strcasecmp(vs[i].str, str)) + return vs[i].value; + } + return -EINVAL; +} + +struct debug_info { + const char *name; + const char *color; + const char *description; + int number; + int position; +}; + +struct debug_context { + struct gsm_lchan *lchan; + struct gsm_subscriber *subscr; + struct gsm_bts *bts; +}; + +static struct debug_context debug_context; +static void *tall_dbg_ctx = NULL; +static LLIST_HEAD(target_list); + +#define DEBUG_CATEGORY(NUMBER, NAME, COLOR, DESCRIPTION) \ + { .name = NAME, .color = COLOR, .description = DESCRIPTION, .number = NUMBER }, + +static const struct debug_info debug_info[] = { + DEBUG_CATEGORY(DRLL, "DRLL", "\033[1;31m", "") + DEBUG_CATEGORY(DCC, "DCC", "\033[1;32m", "") + DEBUG_CATEGORY(DMM, "DMM", "\033[1;33m", "") + DEBUG_CATEGORY(DRR, "DRR", "\033[1;34m", "") + DEBUG_CATEGORY(DRSL, "DRSL", "\033[1;35m", "") + DEBUG_CATEGORY(DNM, "DNM", "\033[1;36m", "") + DEBUG_CATEGORY(DSMS, "DSMS", "\033[1;37m", "") + DEBUG_CATEGORY(DPAG, "DPAG", "\033[1;38m", "") + DEBUG_CATEGORY(DMNCC, "DMNCC","\033[1;39m", "") + DEBUG_CATEGORY(DINP, "DINP", "", "") + DEBUG_CATEGORY(DMI, "DMI", "", "") + DEBUG_CATEGORY(DMIB, "DMIB", "", "") + DEBUG_CATEGORY(DMUX, "DMUX", "", "") + DEBUG_CATEGORY(DMEAS, "DMEAS", "", "") + DEBUG_CATEGORY(DSCCP, "DSCCP", "", "") + DEBUG_CATEGORY(DMSC, "DMSC", "", "") + DEBUG_CATEGORY(DMGCP, "DMGCP", "", "") + DEBUG_CATEGORY(DHO, "DHO", "", "") + DEBUG_CATEGORY(DDB, "DDB", "", "") + DEBUG_CATEGORY(DDB, "DREF", "", "") +}; + +static const struct value_string loglevel_strs[] = { + { 0, "EVERYTHING" }, + { 1, "DEBUG" }, + { 3, "INFO" }, + { 5, "NOTICE" }, + { 7, "ERROR" }, + { 8, "FATAL" }, + { 0, NULL }, +}; + +int debug_parse_level(const char *lvl) +{ + return get_string_value(loglevel_strs, lvl); +} + +int debug_parse_category(const char *category) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(debug_info); ++i) { + if (!strcasecmp(debug_info[i].name+1, category)) + return debug_info[i].number; + } + + return -EINVAL; +} + +/* + * Parse the category mask. + * The format can be this: category1:category2:category3 + * or category1,2:category2,3:... + */ +void debug_parse_category_mask(struct debug_target* target, const char *_mask) +{ + int i = 0; + char *mask = strdup(_mask); + char *category_token = NULL; + + /* Disable everything to enable it afterwards */ + for (i = 0; i < ARRAY_SIZE(target->categories); ++i) + target->categories[i].enabled = 0; + + category_token = strtok(mask, ":"); + do { + for (i = 0; i < ARRAY_SIZE(debug_info); ++i) { + char* colon = strstr(category_token, ","); + int length = strlen(category_token); + + if (colon) + length = colon - category_token; + + if (strncasecmp(debug_info[i].name, category_token, length) == 0) { + int number = debug_info[i].number; + int level = 0; + + if (colon) + level = atoi(colon+1); + + target->categories[number].enabled = 1; + target->categories[number].loglevel = level; + } + } + } while ((category_token = strtok(NULL, ":"))); + + free(mask); +} + +static const char* color(int subsys) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(debug_info); ++i) { + if (debug_info[i].number == subsys) + return debug_info[i].color; + } + + return ""; +} + +static void _output(struct debug_target *target, unsigned int subsys, char *file, int line, + int cont, const char *format, va_list ap) +{ + char col[30]; + char sub[30]; + char tim[30]; + char buf[4096]; + char final[4096]; + + /* prepare the data */ + col[0] = '\0'; + sub[0] = '\0'; + tim[0] = '\0'; + buf[0] = '\0'; + + /* are we using color */ + if (target->use_color) { + snprintf(col, sizeof(col), "%s", color(subsys)); + col[sizeof(col)-1] = '\0'; + } + vsnprintf(buf, sizeof(buf), format, ap); + buf[sizeof(buf)-1] = '\0'; + + if (!cont) { + if (target->print_timestamp) { + char *timestr; + time_t tm; + tm = time(NULL); + timestr = ctime(&tm); + timestr[strlen(timestr)-1] = '\0'; + snprintf(tim, sizeof(tim), "%s ", timestr); + tim[sizeof(tim)-1] = '\0'; + } + snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line); + sub[sizeof(sub)-1] = '\0'; + } + + snprintf(final, sizeof(final), "%s%s%s%s\033[0;m", col, tim, sub, buf); + final[sizeof(final)-1] = '\0'; + target->output(target, final); +} + + +static void _debugp(unsigned int subsys, int level, char *file, int line, + int cont, const char *format, va_list ap) +{ + struct debug_target *tar; + + llist_for_each_entry(tar, &target_list, entry) { + struct debug_category *category; + int output = 0; + + category = &tar->categories[subsys]; + /* subsystem is not supposed to be debugged */ + if (!category->enabled) + continue; + + /* Check the global log level */ + if (tar->loglevel != 0 && level < tar->loglevel) + continue; + + /* Check the category log level */ + if (category->loglevel != 0 && level < category->loglevel) + continue; + + /* + * Apply filters here... if that becomes messy we will need to put + * filters in a list and each filter will say stop, continue, output + */ + if ((tar->filter_map & DEBUG_FILTER_ALL) != 0) { + output = 1; +#if 0 + } else if ((tar->filter_map & DEBUG_FILTER_IMSI) != 0 + && debug_context.subscr && strcmp(debug_context.subscr->imsi, tar->imsi_filter) == 0) { + output = 1; +#endif + } + + if (output) { + /* FIXME: copying the va_list is an ugly workaround against a bug + * hidden somewhere in _output. If we do not copy here, the first + * call to _output() will corrupt the va_list contents, and any + * further _output() calls with the same va_list will segfault */ + va_list bp; + va_copy(bp, ap); + _output(tar, subsys, file, line, cont, format, bp); + va_end(bp); + } + } +} + +void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + _debugp(subsys, LOGL_DEBUG, file, line, cont, format, ap); + va_end(ap); +} + +void debugp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + _debugp(subsys, level, file, line, cont, format, ap); + va_end(ap); +} + +static char hexd_buff[4096]; + +char *hexdump(const unsigned char *buf, int len) +{ + int i; + char *cur = hexd_buff; + + hexd_buff[0] = 0; + for (i = 0; i < len; i++) { + int len_remain = sizeof(hexd_buff) - (cur - hexd_buff); + int rc = snprintf(cur, len_remain, "%02x ", buf[i]); + if (rc <= 0) + break; + cur += rc; + } + hexd_buff[sizeof(hexd_buff)-1] = 0; + return hexd_buff; +} + + + +void debug_add_target(struct debug_target *target) +{ + llist_add_tail(&target->entry, &target_list); +} + +void debug_del_target(struct debug_target *target) +{ + llist_del(&target->entry); +} + +void debug_reset_context(void) +{ + memset(&debug_context, 0, sizeof(debug_context)); +} + +/* currently we are not reffing these */ +void debug_set_context(int ctx, void *value) +{ + switch (ctx) { + case BSC_CTX_LCHAN: + debug_context.lchan = (struct gsm_lchan *) value; + break; + case BSC_CTX_SUBSCR: + debug_context.subscr = (struct gsm_subscriber *) value; + break; + case BSC_CTX_BTS: + debug_context.bts = (struct gsm_bts *) value; + break; + case BSC_CTX_SCCP: + break; + default: + break; + } +} + +void debug_set_imsi_filter(struct debug_target *target, const char *imsi) +{ + if (imsi) { + target->filter_map |= DEBUG_FILTER_IMSI; + target->imsi_filter = talloc_strdup(target, imsi); + } else if (target->imsi_filter) { + target->filter_map &= ~DEBUG_FILTER_IMSI; + talloc_free(target->imsi_filter); + target->imsi_filter = NULL; + } +} + +void debug_set_all_filter(struct debug_target *target, int all) +{ + if (all) + target->filter_map |= DEBUG_FILTER_ALL; + else + target->filter_map &= ~DEBUG_FILTER_ALL; +} + +void debug_set_use_color(struct debug_target *target, int use_color) +{ + target->use_color = use_color; +} + +void debug_set_print_timestamp(struct debug_target *target, int print_timestamp) +{ + target->print_timestamp = print_timestamp; +} + +void debug_set_log_level(struct debug_target *target, int log_level) +{ + target->loglevel = log_level; +} + +void debug_set_category_filter(struct debug_target *target, int category, int enable, int level) +{ + if (category >= Debug_LastEntry) + return; + target->categories[category].enabled = !!enable; + target->categories[category].loglevel = level; +} + +static void _stderr_output(struct debug_target *target, const char *log) +{ + fprintf(target->tgt_stdout.out, "%s", log); + fflush(target->tgt_stdout.out); +} + +struct debug_target *debug_target_create(void) +{ + struct debug_target *target; + + target = talloc_zero(tall_dbg_ctx, struct debug_target); + if (!target) + return NULL; + + INIT_LLIST_HEAD(&target->entry); + memcpy(target->categories, default_categories, sizeof(default_categories)); + target->use_color = 1; + target->print_timestamp = 0; + target->loglevel = 0; + return target; +} + +struct debug_target *debug_target_create_stderr(void) +{ + struct debug_target *target; + + target = debug_target_create(); + if (!target) + return NULL; + + target->tgt_stdout.out = stderr; + target->output = _stderr_output; + return target; +} + +void debug_init(void) +{ + tall_dbg_ctx = talloc_named_const(NULL, 1, "debug"); +} diff --git a/src/host/libosmocom/src/msgb.c b/src/host/libosmocom/src/msgb.c new file mode 100644 index 00000000..2c794125 --- /dev/null +++ b/src/host/libosmocom/src/msgb.c @@ -0,0 +1,97 @@ +/* (C) 2008 by Harald Welte <laforge@gnumonks.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> + +#include <osmocom/msgb.h> +#include <osmocom/talloc.h> +#include <osmocom/debug.h> + +void *tall_msgb_ctx; + +struct msgb *msgb_alloc(u_int16_t size, const char *name) +{ + struct msgb *msg; + + msg = _talloc_zero(tall_msgb_ctx, sizeof(*msg) + size, name); + + if (!msg) { + LOGP(DRSL, LOGL_FATAL, "unable to allocate msgb\n"); + return NULL; + } + + msg->data_len = size; + msg->len = 0; + msg->data = msg->_data; + + msg->head = msg->data; + msg->data = msg->data; + /* reset tail pointer */ + msg->tail = msg->data; + //msg->end = msg->tail + size; + + return msg; +} + +void msgb_free(struct msgb *m) +{ + talloc_free(m); +} + +void msgb_enqueue(struct llist_head *queue, struct msgb *msg) +{ + llist_add_tail(&msg->list, queue); +} + +struct msgb *msgb_dequeue(struct llist_head *queue) +{ + struct llist_head *lh; + + if (llist_empty(queue)) + return NULL; + + lh = queue->next; + llist_del(lh); + + return llist_entry(lh, struct msgb, list); +} + +void msgb_reset(struct msgb *msg) +{ + msg->len = 0; + msg->len = 0; + msg->data = msg->_data; + + msg->head = msg->data; + msg->data = msg->data; + /* reset tail pointer */ + msg->tail = msg->data; + + /* reset pointers */ + msg->bts_link = NULL; + msg->trx = NULL; + msg->lchan = NULL; + msg->l2h = NULL; + msg->l3h = NULL; + msg->smsh = NULL; +} diff --git a/src/host/libosmocom/src/select.c b/src/host/libosmocom/src/select.c new file mode 100644 index 00000000..c212cc79 --- /dev/null +++ b/src/host/libosmocom/src/select.c @@ -0,0 +1,124 @@ +/* select filedescriptor handling, taken from: + * userspace logging daemon for the iptables ULOG target + * of the linux 2.4 netfilter subsystem. + * + * (C) 2000-2009 by Harald Welte <laforge@gnumonks.org> + * + * 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <fcntl.h> +#include <osmocom/select.h> +#include <osmocom/linuxlist.h> +#include <osmocom/timer.h> + +static int maxfd = 0; +static LLIST_HEAD(bsc_fds); +static int unregistered_count; + +int bsc_register_fd(struct bsc_fd *fd) +{ + int flags; + + /* make FD nonblocking */ + flags = fcntl(fd->fd, F_GETFL); + if (flags < 0) + return flags; + flags |= O_NONBLOCK; + flags = fcntl(fd->fd, F_SETFL, flags); + if (flags < 0) + return flags; + + /* Register FD */ + if (fd->fd > maxfd) + maxfd = fd->fd; + + llist_add_tail(&fd->list, &bsc_fds); + + return 0; +} + +void bsc_unregister_fd(struct bsc_fd *fd) +{ + unregistered_count++; + llist_del(&fd->list); +} + +int bsc_select_main(int polling) +{ + struct bsc_fd *ufd, *tmp; + fd_set readset, writeset, exceptset; + int work = 0, rc; + struct timeval no_time = {0, 0}; + + FD_ZERO(&readset); + FD_ZERO(&writeset); + FD_ZERO(&exceptset); + + /* prepare read and write fdsets */ + llist_for_each_entry(ufd, &bsc_fds, list) { + if (ufd->when & BSC_FD_READ) + FD_SET(ufd->fd, &readset); + + if (ufd->when & BSC_FD_WRITE) + FD_SET(ufd->fd, &writeset); + + if (ufd->when & BSC_FD_EXCEPT) + FD_SET(ufd->fd, &exceptset); + } + + bsc_timer_check(); + + if (!polling) + bsc_prepare_timers(); + rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : bsc_nearest_timer()); + if (rc < 0) + return 0; + + /* fire timers */ + bsc_update_timers(); + + /* call registered callback functions */ +restart: + unregistered_count = 0; + llist_for_each_entry_safe(ufd, tmp, &bsc_fds, list) { + int flags = 0; + + if (FD_ISSET(ufd->fd, &readset)) { + flags |= BSC_FD_READ; + FD_CLR(ufd->fd, &readset); + } + + if (FD_ISSET(ufd->fd, &writeset)) { + flags |= BSC_FD_WRITE; + FD_CLR(ufd->fd, &writeset); + } + + if (FD_ISSET(ufd->fd, &exceptset)) { + flags |= BSC_FD_EXCEPT; + FD_CLR(ufd->fd, &exceptset); + } + + if (flags) { + work = 1; + ufd->cb(ufd, flags); + } + /* ugly, ugly hack. If more than one filedescriptors were + * unregistered, they might have been consecutive and + * llist_for_each_entry_safe() is no longer safe */ + if (unregistered_count > 1) + goto restart; + } + return work; +} diff --git a/src/host/libosmocom/src/signal.c b/src/host/libosmocom/src/signal.c new file mode 100644 index 00000000..d45e94e6 --- /dev/null +++ b/src/host/libosmocom/src/signal.c @@ -0,0 +1,83 @@ +/* Generic signalling/notification infrastructure */ +/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/signal.h> +#include <osmocom/talloc.h> +#include <stdlib.h> +#include <string.h> + + +void *tall_sigh_ctx; +static LLIST_HEAD(signal_handler_list); + +struct signal_handler { + struct llist_head entry; + unsigned int subsys; + signal_cbfn *cbfn; + void *data; +}; + + +int register_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data) +{ + struct signal_handler *sig_data; + + sig_data = talloc(tall_sigh_ctx, struct signal_handler); + if (!sig_data) + return -ENOMEM; + + memset(sig_data, 0, sizeof(*sig_data)); + + sig_data->subsys = subsys; + sig_data->data = data; + sig_data->cbfn = cbfn; + + /* FIXME: check if we already have a handler for this subsys/cbfn/data */ + + llist_add_tail(&sig_data->entry, &signal_handler_list); + + return 0; +} + +void unregister_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data) +{ + struct signal_handler *handler; + + llist_for_each_entry(handler, &signal_handler_list, entry) { + if (handler->cbfn == cbfn && handler->data == data + && subsys == handler->subsys) { + llist_del(&handler->entry); + talloc_free(handler); + break; + } + } +} + + +void dispatch_signal(unsigned int subsys, unsigned int signal, void *signal_data) +{ + struct signal_handler *handler; + + llist_for_each_entry(handler, &signal_handler_list, entry) { + if (handler->subsys != subsys) + continue; + (*handler->cbfn)(subsys, signal, handler->data, signal_data); + } +} diff --git a/src/host/libosmocom/src/talloc.c b/src/host/libosmocom/src/talloc.c new file mode 100644 index 00000000..5b8bc274 --- /dev/null +++ b/src/host/libosmocom/src/talloc.c @@ -0,0 +1,1805 @@ +/* + Samba Unix SMB/CIFS implementation. + + Samba trivial allocation library - new interface + + NOTE: Please read talloc_guide.txt for full documentation + + Copyright (C) Andrew Tridgell 2004 + Copyright (C) Stefan Metzmacher 2006 + + ** NOTE! The following LGPL license applies to the talloc + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +/* + inspired by http://swapped.cc/halloc/ +*/ + +#ifdef _SAMBA_BUILD_ +#include "version.h" +#if (SAMBA_VERSION_MAJOR<4) +#include "includes.h" +/* This is to circumvent SAMBA3's paranoid malloc checker. Here in this file + * we trust ourselves... */ +#ifdef malloc +#undef malloc +#endif +#ifdef realloc +#undef realloc +#endif +#define _TALLOC_SAMBA3 +#endif /* (SAMBA_VERSION_MAJOR<4) */ +#endif /* _SAMBA_BUILD_ */ + +#ifndef _TALLOC_SAMBA3 +//#include "replace.h" +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> +#include <stdbool.h> +#define __USE_GNU +#include <string.h> +#undef __USE_GNU +#include <osmocom/talloc.h> +#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#endif /* not _TALLOC_SAMBA3 */ + +/* use this to force every realloc to change the pointer, to stress test + code that might not cope */ +#define ALWAYS_REALLOC 0 + + +#define MAX_TALLOC_SIZE 0x10000000 +#define TALLOC_MAGIC 0xe814ec70 +#define TALLOC_FLAG_FREE 0x01 +#define TALLOC_FLAG_LOOP 0x02 +#define TALLOC_FLAG_POOL 0x04 /* This is a talloc pool */ +#define TALLOC_FLAG_POOLMEM 0x08 /* This is allocated in a pool */ +#define TALLOC_MAGIC_REFERENCE ((const char *)1) + +/* by default we abort when given a bad pointer (such as when talloc_free() is called + on a pointer that came from malloc() */ +#ifndef TALLOC_ABORT +#define TALLOC_ABORT(reason) abort() +#endif + +#ifndef discard_const_p +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) +# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr))) +#else +# define discard_const_p(type, ptr) ((type *)(ptr)) +#endif +#endif + +/* these macros gain us a few percent of speed on gcc */ +#if (__GNUC__ >= 3) +/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1 + as its first argument */ +#ifndef likely +#define likely(x) __builtin_expect(!!(x), 1) +#endif +#ifndef unlikely +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif +#else +#ifndef likely +#define likely(x) (x) +#endif +#ifndef unlikely +#define unlikely(x) (x) +#endif +#endif + +#ifdef __APPLE__ +/* taken from http://insanecoding.blogspot.com/2007/03/methods-for-safe-string-handling.html */ +size_t strnlen(const char *s, size_t n) +{ + const char *p = (const char *)memchr(s, 0, n); + return(p ? p-s : n); +} +#endif + +/* this null_context is only used if talloc_enable_leak_report() or + talloc_enable_leak_report_full() is called, otherwise it remains + NULL +*/ +static void *null_context; +static void *autofree_context; + +struct talloc_reference_handle { + struct talloc_reference_handle *next, *prev; + void *ptr; +}; + +typedef int (*talloc_destructor_t)(void *); + +struct talloc_chunk { + struct talloc_chunk *next, *prev; + struct talloc_chunk *parent, *child; + struct talloc_reference_handle *refs; + talloc_destructor_t destructor; + const char *name; + size_t size; + unsigned flags; + + /* + * "pool" has dual use: + * + * For the talloc pool itself (i.e. TALLOC_FLAG_POOL is set), "pool" + * marks the end of the currently allocated area. + * + * For members of the pool (i.e. TALLOC_FLAG_POOLMEM is set), "pool" + * is a pointer to the struct talloc_chunk of the pool that it was + * allocated from. This way children can quickly find the pool to chew + * from. + */ + void *pool; +}; + +/* 16 byte alignment seems to keep everyone happy */ +#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15) +#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc)) + +static void (*talloc_abort_fn)(const char *reason); + +void talloc_set_abort_fn(void (*abort_fn)(const char *reason)) +{ + talloc_abort_fn = abort_fn; +} + +static void talloc_abort(const char *reason) +{ + if (!talloc_abort_fn) { + TALLOC_ABORT(reason); + } + + talloc_abort_fn(reason); +} + +static void talloc_abort_double_free(void) +{ + talloc_abort("Bad talloc magic value - double free"); +} + +static void talloc_abort_unknown_value(void) +{ + talloc_abort("Bad talloc magic value - unknown value"); +} + +/* panic if we get a bad magic value */ +static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr) +{ + const char *pp = (const char *)ptr; + struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE); + if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) { + if (tc->flags & TALLOC_FLAG_FREE) { + talloc_abort_double_free(); + } else { + talloc_abort_unknown_value(); + } + } + return tc; +} + +/* hook into the front of the list */ +#define _TLIST_ADD(list, p) \ +do { \ + if (!(list)) { \ + (list) = (p); \ + (p)->next = (p)->prev = NULL; \ + } else { \ + (list)->prev = (p); \ + (p)->next = (list); \ + (p)->prev = NULL; \ + (list) = (p); \ + }\ +} while (0) + +/* remove an element from a list - element doesn't have to be in list. */ +#define _TLIST_REMOVE(list, p) \ +do { \ + if ((p) == (list)) { \ + (list) = (p)->next; \ + if (list) (list)->prev = NULL; \ + } else { \ + if ((p)->prev) (p)->prev->next = (p)->next; \ + if ((p)->next) (p)->next->prev = (p)->prev; \ + } \ + if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \ +} while (0) + + +/* + return the parent chunk of a pointer +*/ +static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return NULL; + } + + tc = talloc_chunk_from_ptr(ptr); + while (tc->prev) tc=tc->prev; + + return tc->parent; +} + +void *talloc_parent(const void *ptr) +{ + struct talloc_chunk *tc = talloc_parent_chunk(ptr); + return tc? TC_PTR_FROM_CHUNK(tc) : NULL; +} + +/* + find parents name +*/ +const char *talloc_parent_name(const void *ptr) +{ + struct talloc_chunk *tc = talloc_parent_chunk(ptr); + return tc? tc->name : NULL; +} + +/* + A pool carries an in-pool object count count in the first 16 bytes. + bytes. This is done to support talloc_steal() to a parent outside of the + pool. The count includes the pool itself, so a talloc_free() on a pool will + only destroy the pool if the count has dropped to zero. A talloc_free() of a + pool member will reduce the count, and eventually also call free(3) on the + pool memory. + + The object count is not put into "struct talloc_chunk" because it is only + relevant for talloc pools and the alignment to 16 bytes would increase the + memory footprint of each talloc chunk by those 16 bytes. +*/ + +#define TALLOC_POOL_HDR_SIZE 16 + +static unsigned int *talloc_pool_objectcount(struct talloc_chunk *tc) +{ + return (unsigned int *)((char *)tc + sizeof(struct talloc_chunk)); +} + +/* + Allocate from a pool +*/ + +static struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent, + size_t size) +{ + struct talloc_chunk *pool_ctx = NULL; + size_t space_left; + struct talloc_chunk *result; + size_t chunk_size; + + if (parent == NULL) { + return NULL; + } + + if (parent->flags & TALLOC_FLAG_POOL) { + pool_ctx = parent; + } + else if (parent->flags & TALLOC_FLAG_POOLMEM) { + pool_ctx = (struct talloc_chunk *)parent->pool; + } + + if (pool_ctx == NULL) { + return NULL; + } + + space_left = ((char *)pool_ctx + TC_HDR_SIZE + pool_ctx->size) + - ((char *)pool_ctx->pool); + + /* + * Align size to 16 bytes + */ + chunk_size = ((size + 15) & ~15); + + if (space_left < chunk_size) { + return NULL; + } + + result = (struct talloc_chunk *)pool_ctx->pool; + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED) + VALGRIND_MAKE_MEM_UNDEFINED(result, size); +#endif + + pool_ctx->pool = (void *)((char *)result + chunk_size); + + result->flags = TALLOC_MAGIC | TALLOC_FLAG_POOLMEM; + result->pool = pool_ctx; + + *talloc_pool_objectcount(pool_ctx) += 1; + + return result; +} + +/* + Allocate a bit of memory as a child of an existing pointer +*/ +static inline void *__talloc(const void *context, size_t size) +{ + struct talloc_chunk *tc = NULL; + + if (unlikely(context == NULL)) { + context = null_context; + } + + if (unlikely(size >= MAX_TALLOC_SIZE)) { + return NULL; + } + + if (context != NULL) { + tc = talloc_alloc_pool(talloc_chunk_from_ptr(context), + TC_HDR_SIZE+size); + } + + if (tc == NULL) { + tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size); + if (unlikely(tc == NULL)) return NULL; + tc->flags = TALLOC_MAGIC; + tc->pool = NULL; + } + + tc->size = size; + tc->destructor = NULL; + tc->child = NULL; + tc->name = NULL; + tc->refs = NULL; + + if (likely(context)) { + struct talloc_chunk *parent = talloc_chunk_from_ptr(context); + + if (parent->child) { + parent->child->parent = NULL; + tc->next = parent->child; + tc->next->prev = tc; + } else { + tc->next = NULL; + } + tc->parent = parent; + tc->prev = NULL; + parent->child = tc; + } else { + tc->next = tc->prev = tc->parent = NULL; + } + + return TC_PTR_FROM_CHUNK(tc); +} + +/* + * Create a talloc pool + */ + +void *talloc_pool(const void *context, size_t size) +{ + void *result = __talloc(context, size + TALLOC_POOL_HDR_SIZE); + struct talloc_chunk *tc; + + if (unlikely(result == NULL)) { + return NULL; + } + + tc = talloc_chunk_from_ptr(result); + + tc->flags |= TALLOC_FLAG_POOL; + tc->pool = (char *)result + TALLOC_POOL_HDR_SIZE; + + *talloc_pool_objectcount(tc) = 1; + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) + VALGRIND_MAKE_MEM_NOACCESS(tc->pool, size); +#endif + + return result; +} + +/* + setup a destructor to be called on free of a pointer + the destructor should return 0 on success, or -1 on failure. + if the destructor fails then the free is failed, and the memory can + be continued to be used +*/ +void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->destructor = destructor; +} + +/* + increase the reference count on a piece of memory. +*/ +int talloc_increase_ref_count(const void *ptr) +{ + if (unlikely(!talloc_reference(null_context, ptr))) { + return -1; + } + return 0; +} + +/* + helper for talloc_reference() + + this is referenced by a function pointer and should not be inline +*/ +static int talloc_reference_destructor(struct talloc_reference_handle *handle) +{ + struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr); + _TLIST_REMOVE(ptr_tc->refs, handle); + return 0; +} + +/* + more efficient way to add a name to a pointer - the name must point to a + true string constant +*/ +static inline void _talloc_set_name_const(const void *ptr, const char *name) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->name = name; +} + +/* + internal talloc_named_const() +*/ +static inline void *_talloc_named_const(const void *context, size_t size, const char *name) +{ + void *ptr; + + ptr = __talloc(context, size); + if (unlikely(ptr == NULL)) { + return NULL; + } + + _talloc_set_name_const(ptr, name); + + return ptr; +} + +/* + make a secondary reference to a pointer, hanging off the given context. + the pointer remains valid until both the original caller and this given + context are freed. + + the major use for this is when two different structures need to reference the + same underlying data, and you want to be able to free the two instances separately, + and in either order +*/ +void *_talloc_reference(const void *context, const void *ptr) +{ + struct talloc_chunk *tc; + struct talloc_reference_handle *handle; + if (unlikely(ptr == NULL)) return NULL; + + tc = talloc_chunk_from_ptr(ptr); + handle = (struct talloc_reference_handle *)_talloc_named_const(context, + sizeof(struct talloc_reference_handle), + TALLOC_MAGIC_REFERENCE); + if (unlikely(handle == NULL)) return NULL; + + /* note that we hang the destructor off the handle, not the + main context as that allows the caller to still setup their + own destructor on the context if they want to */ + talloc_set_destructor(handle, talloc_reference_destructor); + handle->ptr = discard_const_p(void, ptr); + _TLIST_ADD(tc->refs, handle); + return handle->ptr; +} + + +/* + internal talloc_free call +*/ +static inline int _talloc_free(void *ptr) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return -1; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (unlikely(tc->refs)) { + int is_child; + /* check this is a reference from a child or grantchild + * back to it's parent or grantparent + * + * in that case we need to remove the reference and + * call another instance of talloc_free() on the current + * pointer. + */ + is_child = talloc_is_parent(tc->refs, ptr); + _talloc_free(tc->refs); + if (is_child) { + return _talloc_free(ptr); + } + return -1; + } + + if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) { + /* we have a free loop - stop looping */ + return 0; + } + + if (unlikely(tc->destructor)) { + talloc_destructor_t d = tc->destructor; + if (d == (talloc_destructor_t)-1) { + return -1; + } + tc->destructor = (talloc_destructor_t)-1; + if (d(ptr) == -1) { + tc->destructor = d; + return -1; + } + tc->destructor = NULL; + } + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->flags |= TALLOC_FLAG_LOOP; + + while (tc->child) { + /* we need to work out who will own an abandoned child + if it cannot be freed. In priority order, the first + choice is owner of any remaining reference to this + pointer, the second choice is our parent, and the + final choice is the null context. */ + void *child = TC_PTR_FROM_CHUNK(tc->child); + const void *new_parent = null_context; + if (unlikely(tc->child->refs)) { + struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + if (unlikely(_talloc_free(child) == -1)) { + if (new_parent == null_context) { + struct talloc_chunk *p = talloc_parent_chunk(ptr); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + talloc_steal(new_parent, child); + } + } + + tc->flags |= TALLOC_FLAG_FREE; + + if (tc->flags & (TALLOC_FLAG_POOL|TALLOC_FLAG_POOLMEM)) { + struct talloc_chunk *pool; + unsigned int *pool_object_count; + + pool = (tc->flags & TALLOC_FLAG_POOL) + ? tc : (struct talloc_chunk *)tc->pool; + + pool_object_count = talloc_pool_objectcount(pool); + + if (*pool_object_count == 0) { + talloc_abort("Pool object count zero!"); + } + + *pool_object_count -= 1; + + if (*pool_object_count == 0) { + free(pool); + } + } + else { + free(tc); + } + return 0; +} + +/* + move a lump of memory from one talloc context to another return the + ptr on success, or NULL if it could not be transferred. + passing NULL as ptr will always return NULL with no side effects. +*/ +void *_talloc_steal(const void *new_ctx, const void *ptr) +{ + struct talloc_chunk *tc, *new_tc; + + if (unlikely(!ptr)) { + return NULL; + } + + if (unlikely(new_ctx == NULL)) { + new_ctx = null_context; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (unlikely(new_ctx == NULL)) { + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->parent = tc->next = tc->prev = NULL; + return discard_const_p(void, ptr); + } + + new_tc = talloc_chunk_from_ptr(new_ctx); + + if (unlikely(tc == new_tc || tc->parent == new_tc)) { + return discard_const_p(void, ptr); + } + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->parent = new_tc; + if (new_tc->child) new_tc->child->parent = NULL; + _TLIST_ADD(new_tc->child, tc); + + return discard_const_p(void, ptr); +} + + + +/* + remove a secondary reference to a pointer. This undo's what + talloc_reference() has done. The context and pointer arguments + must match those given to a talloc_reference() +*/ +static inline int talloc_unreference(const void *context, const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + + if (unlikely(context == NULL)) { + context = null_context; + } + + for (h=tc->refs;h;h=h->next) { + struct talloc_chunk *p = talloc_parent_chunk(h); + if (p == NULL) { + if (context == NULL) break; + } else if (TC_PTR_FROM_CHUNK(p) == context) { + break; + } + } + if (h == NULL) { + return -1; + } + + return _talloc_free(h); +} + +/* + remove a specific parent context from a pointer. This is a more + controlled varient of talloc_free() +*/ +int talloc_unlink(const void *context, void *ptr) +{ + struct talloc_chunk *tc_p, *new_p; + void *new_parent; + + if (ptr == NULL) { + return -1; + } + + if (context == NULL) { + context = null_context; + } + + if (talloc_unreference(context, ptr) == 0) { + return 0; + } + + if (context == NULL) { + if (talloc_parent_chunk(ptr) != NULL) { + return -1; + } + } else { + if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) { + return -1; + } + } + + tc_p = talloc_chunk_from_ptr(ptr); + + if (tc_p->refs == NULL) { + return _talloc_free(ptr); + } + + new_p = talloc_parent_chunk(tc_p->refs); + if (new_p) { + new_parent = TC_PTR_FROM_CHUNK(new_p); + } else { + new_parent = NULL; + } + + if (talloc_unreference(new_parent, ptr) != 0) { + return -1; + } + + talloc_steal(new_parent, ptr); + + return 0; +} + +/* + add a name to an existing pointer - va_list version +*/ +static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->name = talloc_vasprintf(ptr, fmt, ap); + if (likely(tc->name)) { + _talloc_set_name_const(tc->name, ".name"); + } + return tc->name; +} + +/* + add a name to an existing pointer +*/ +const char *talloc_set_name(const void *ptr, const char *fmt, ...) +{ + const char *name; + va_list ap; + va_start(ap, fmt); + name = talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + return name; +} + + +/* + create a named talloc pointer. Any talloc pointer can be named, and + talloc_named() operates just like talloc() except that it allows you + to name the pointer. +*/ +void *talloc_named(const void *context, size_t size, const char *fmt, ...) +{ + va_list ap; + void *ptr; + const char *name; + + ptr = __talloc(context, size); + if (unlikely(ptr == NULL)) return NULL; + + va_start(ap, fmt); + name = talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + + if (unlikely(name == NULL)) { + _talloc_free(ptr); + return NULL; + } + + return ptr; +} + +/* + return the name of a talloc ptr, or "UNNAMED" +*/ +const char *talloc_get_name(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) { + return ".reference"; + } + if (likely(tc->name)) { + return tc->name; + } + return "UNNAMED"; +} + + +/* + check if a pointer has the given name. If it does, return the pointer, + otherwise return NULL +*/ +void *talloc_check_name(const void *ptr, const char *name) +{ + const char *pname; + if (unlikely(ptr == NULL)) return NULL; + pname = talloc_get_name(ptr); + if (likely(pname == name || strcmp(pname, name) == 0)) { + return discard_const_p(void, ptr); + } + return NULL; +} + +static void talloc_abort_type_missmatch(const char *location, + const char *name, + const char *expected) +{ + const char *reason; + + reason = talloc_asprintf(NULL, + "%s: Type mismatch: name[%s] expected[%s]", + location, + name?name:"NULL", + expected); + if (!reason) { + reason = "Type mismatch"; + } + + talloc_abort(reason); +} + +void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location) +{ + const char *pname; + + if (unlikely(ptr == NULL)) { + talloc_abort_type_missmatch(location, NULL, name); + return NULL; + } + + pname = talloc_get_name(ptr); + if (likely(pname == name || strcmp(pname, name) == 0)) { + return discard_const_p(void, ptr); + } + + talloc_abort_type_missmatch(location, pname, name); + return NULL; +} + +/* + this is for compatibility with older versions of talloc +*/ +void *talloc_init(const char *fmt, ...) +{ + va_list ap; + void *ptr; + const char *name; + + /* + * samba3 expects talloc_report_depth_cb(NULL, ...) + * reports all talloc'ed memory, so we need to enable + * null_tracking + */ + talloc_enable_null_tracking(); + + ptr = __talloc(NULL, 0); + if (unlikely(ptr == NULL)) return NULL; + + va_start(ap, fmt); + name = talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + + if (unlikely(name == NULL)) { + _talloc_free(ptr); + return NULL; + } + + return ptr; +} + +/* + this is a replacement for the Samba3 talloc_destroy_pool functionality. It + should probably not be used in new code. It's in here to keep the talloc + code consistent across Samba 3 and 4. +*/ +void talloc_free_children(void *ptr) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return; + } + + tc = talloc_chunk_from_ptr(ptr); + + while (tc->child) { + /* we need to work out who will own an abandoned child + if it cannot be freed. In priority order, the first + choice is owner of any remaining reference to this + pointer, the second choice is our parent, and the + final choice is the null context. */ + void *child = TC_PTR_FROM_CHUNK(tc->child); + const void *new_parent = null_context; + if (unlikely(tc->child->refs)) { + struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + if (unlikely(_talloc_free(child) == -1)) { + if (new_parent == null_context) { + struct talloc_chunk *p = talloc_parent_chunk(ptr); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + talloc_steal(new_parent, child); + } + } + + if ((tc->flags & TALLOC_FLAG_POOL) + && (*talloc_pool_objectcount(tc) == 1)) { + tc->pool = ((char *)tc + TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE); +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) + VALGRIND_MAKE_MEM_NOACCESS( + tc->pool, tc->size - TALLOC_POOL_HDR_SIZE); +#endif + } +} + +/* + Allocate a bit of memory as a child of an existing pointer +*/ +void *_talloc(const void *context, size_t size) +{ + return __talloc(context, size); +} + +/* + externally callable talloc_set_name_const() +*/ +void talloc_set_name_const(const void *ptr, const char *name) +{ + _talloc_set_name_const(ptr, name); +} + +/* + create a named talloc pointer. Any talloc pointer can be named, and + talloc_named() operates just like talloc() except that it allows you + to name the pointer. +*/ +void *talloc_named_const(const void *context, size_t size, const char *name) +{ + return _talloc_named_const(context, size, name); +} + +/* + free a talloc pointer. This also frees all child pointers of this + pointer recursively + + return 0 if the memory is actually freed, otherwise -1. The memory + will not be freed if the ref_count is > 1 or the destructor (if + any) returns non-zero +*/ +int talloc_free(void *ptr) +{ + return _talloc_free(ptr); +} + + + +/* + A talloc version of realloc. The context argument is only used if + ptr is NULL +*/ +void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name) +{ + struct talloc_chunk *tc; + void *new_ptr; + bool malloced = false; + + /* size zero is equivalent to free() */ + if (unlikely(size == 0)) { + _talloc_free(ptr); + return NULL; + } + + if (unlikely(size >= MAX_TALLOC_SIZE)) { + return NULL; + } + + /* realloc(NULL) is equivalent to malloc() */ + if (ptr == NULL) { + return _talloc_named_const(context, size, name); + } + + tc = talloc_chunk_from_ptr(ptr); + + /* don't allow realloc on referenced pointers */ + if (unlikely(tc->refs)) { + return NULL; + } + + /* don't let anybody try to realloc a talloc_pool */ + if (unlikely(tc->flags & TALLOC_FLAG_POOL)) { + return NULL; + } + + /* don't shrink if we have less than 1k to gain */ + if ((size < tc->size) && ((tc->size - size) < 1024)) { + tc->size = size; + return ptr; + } + + /* by resetting magic we catch users of the old memory */ + tc->flags |= TALLOC_FLAG_FREE; + +#if ALWAYS_REALLOC + new_ptr = malloc(size + TC_HDR_SIZE); + if (new_ptr) { + memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE); + free(tc); + } +#else + if (tc->flags & TALLOC_FLAG_POOLMEM) { + + new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE); + *talloc_pool_objectcount((struct talloc_chunk *) + (tc->pool)) -= 1; + + if (new_ptr == NULL) { + new_ptr = malloc(TC_HDR_SIZE+size); + malloced = true; + } + + if (new_ptr) { + memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE); + } + } + else { + new_ptr = realloc(tc, size + TC_HDR_SIZE); + } +#endif + if (unlikely(!new_ptr)) { + tc->flags &= ~TALLOC_FLAG_FREE; + return NULL; + } + + tc = (struct talloc_chunk *)new_ptr; + tc->flags &= ~TALLOC_FLAG_FREE; + if (malloced) { + tc->flags &= ~TALLOC_FLAG_POOLMEM; + } + if (tc->parent) { + tc->parent->child = tc; + } + if (tc->child) { + tc->child->parent = tc; + } + + if (tc->prev) { + tc->prev->next = tc; + } + if (tc->next) { + tc->next->prev = tc; + } + + tc->size = size; + _talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name); + + return TC_PTR_FROM_CHUNK(tc); +} + +/* + a wrapper around talloc_steal() for situations where you are moving a pointer + between two structures, and want the old pointer to be set to NULL +*/ +void *_talloc_move(const void *new_ctx, const void *_pptr) +{ + const void **pptr = discard_const_p(const void *,_pptr); + void *ret = _talloc_steal(new_ctx, *pptr); + (*pptr) = NULL; + return ret; +} + +/* + return the total size of a talloc pool (subtree) +*/ +size_t talloc_total_size(const void *ptr) +{ + size_t total = 0; + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) { + return 0; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (tc->flags & TALLOC_FLAG_LOOP) { + return 0; + } + + tc->flags |= TALLOC_FLAG_LOOP; + + total = tc->size; + for (c=tc->child;c;c=c->next) { + total += talloc_total_size(TC_PTR_FROM_CHUNK(c)); + } + + tc->flags &= ~TALLOC_FLAG_LOOP; + + return total; +} + +/* + return the total number of blocks in a talloc pool (subtree) +*/ +size_t talloc_total_blocks(const void *ptr) +{ + size_t total = 0; + struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); + + if (tc->flags & TALLOC_FLAG_LOOP) { + return 0; + } + + tc->flags |= TALLOC_FLAG_LOOP; + + total++; + for (c=tc->child;c;c=c->next) { + total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c)); + } + + tc->flags &= ~TALLOC_FLAG_LOOP; + + return total; +} + +/* + return the number of external references to a pointer +*/ +size_t talloc_reference_count(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + size_t ret = 0; + + for (h=tc->refs;h;h=h->next) { + ret++; + } + return ret; +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, + void (*callback)(const void *ptr, + int depth, int max_depth, + int is_ref, + void *private_data), + void *private_data) +{ + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) return; + + tc = talloc_chunk_from_ptr(ptr); + + if (tc->flags & TALLOC_FLAG_LOOP) { + return; + } + + callback(ptr, depth, max_depth, 0, private_data); + + if (max_depth >= 0 && depth >= max_depth) { + return; + } + + tc->flags |= TALLOC_FLAG_LOOP; + for (c=tc->child;c;c=c->next) { + if (c->name == TALLOC_MAGIC_REFERENCE) { + struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c); + callback(h->ptr, depth + 1, max_depth, 1, private_data); + } else { + talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data); + } + } + tc->flags &= ~TALLOC_FLAG_LOOP; +} + +static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f) +{ + const char *name = talloc_get_name(ptr); + FILE *f = (FILE *)_f; + + if (is_ref) { + fprintf(f, "%*sreference to: %s\n", depth*4, "", name); + return; + } + + if (depth == 0) { + fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", + (max_depth < 0 ? "full " :""), name, + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr)); + return; + } + + fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", + depth*4, "", + name, + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr), + (int)talloc_reference_count(ptr), ptr); + +#if 0 + fprintf(f, "content: "); + if (talloc_total_size(ptr)) { + int tot = talloc_total_size(ptr); + int i; + + for (i = 0; i < tot; i++) { + if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) { + fprintf(f, "%c", ((char *)ptr)[i]); + } else { + fprintf(f, "~%02x", ((char *)ptr)[i]); + } + } + } + fprintf(f, "\n"); +#endif +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f) +{ + talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f); + fflush(f); +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_full(const void *ptr, FILE *f) +{ + talloc_report_depth_file(ptr, 0, -1, f); +} + +/* + report on memory usage by all children of a pointer +*/ +void talloc_report(const void *ptr, FILE *f) +{ + talloc_report_depth_file(ptr, 0, 1, f); +} + +/* + report on any memory hanging off the null context +*/ +static void talloc_report_null(void) +{ + if (talloc_total_size(null_context) != 0) { + talloc_report(null_context, stderr); + } +} + +/* + report on any memory hanging off the null context +*/ +static void talloc_report_null_full(void) +{ + if (talloc_total_size(null_context) != 0) { + talloc_report_full(null_context, stderr); + } +} + +/* + enable tracking of the NULL context +*/ +void talloc_enable_null_tracking(void) +{ + if (null_context == NULL) { + null_context = _talloc_named_const(NULL, 0, "null_context"); + } +} + +/* + disable tracking of the NULL context +*/ +void talloc_disable_null_tracking(void) +{ + _talloc_free(null_context); + null_context = NULL; +} + +/* + enable leak reporting on exit +*/ +void talloc_enable_leak_report(void) +{ + talloc_enable_null_tracking(); + atexit(talloc_report_null); +} + +/* + enable full leak reporting on exit +*/ +void talloc_enable_leak_report_full(void) +{ + talloc_enable_null_tracking(); + atexit(talloc_report_null_full); +} + +/* + talloc and zero memory. +*/ +void *_talloc_zero(const void *ctx, size_t size, const char *name) +{ + void *p = _talloc_named_const(ctx, size, name); + + if (p) { + memset(p, '\0', size); + } + + return p; +} + +/* + memdup with a talloc. +*/ +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name) +{ + void *newp = _talloc_named_const(t, size, name); + + if (likely(newp)) { + memcpy(newp, p, size); + } + + return newp; +} + +static inline char *__talloc_strlendup(const void *t, const char *p, size_t len) +{ + char *ret; + + ret = (char *)__talloc(t, len + 1); + if (unlikely(!ret)) return NULL; + + memcpy(ret, p, len); + ret[len] = 0; + + _talloc_set_name_const(ret, ret); + return ret; +} + +/* + strdup with a talloc +*/ +char *talloc_strdup(const void *t, const char *p) +{ + if (unlikely(!p)) return NULL; + return __talloc_strlendup(t, p, strlen(p)); +} + +/* + strndup with a talloc +*/ +char *talloc_strndup(const void *t, const char *p, size_t n) +{ + if (unlikely(!p)) return NULL; + return __talloc_strlendup(t, p, strnlen(p, n)); +} + +static inline char *__talloc_strlendup_append(char *s, size_t slen, + const char *a, size_t alen) +{ + char *ret; + + ret = talloc_realloc(NULL, s, char, slen + alen + 1); + if (unlikely(!ret)) return NULL; + + /* append the string and the trailing \0 */ + memcpy(&ret[slen], a, alen); + ret[slen+alen] = 0; + + _talloc_set_name_const(ret, ret); + return ret; +} + +/* + * Appends at the end of the string. + */ +char *talloc_strdup_append(char *s, const char *a) +{ + if (unlikely(!s)) { + return talloc_strdup(NULL, a); + } + + if (unlikely(!a)) { + return s; + } + + return __talloc_strlendup_append(s, strlen(s), a, strlen(a)); +} + +/* + * Appends at the end of the talloc'ed buffer, + * not the end of the string. + */ +char *talloc_strdup_append_buffer(char *s, const char *a) +{ + size_t slen; + + if (unlikely(!s)) { + return talloc_strdup(NULL, a); + } + + if (unlikely(!a)) { + return s; + } + + slen = talloc_get_size(s); + if (likely(slen > 0)) { + slen--; + } + + return __talloc_strlendup_append(s, slen, a, strlen(a)); +} + +/* + * Appends at the end of the string. + */ +char *talloc_strndup_append(char *s, const char *a, size_t n) +{ + if (unlikely(!s)) { + return talloc_strdup(NULL, a); + } + + if (unlikely(!a)) { + return s; + } + + return __talloc_strlendup_append(s, strlen(s), a, strnlen(a, n)); +} + +/* + * Appends at the end of the talloc'ed buffer, + * not the end of the string. + */ +char *talloc_strndup_append_buffer(char *s, const char *a, size_t n) +{ + size_t slen; + + if (unlikely(!s)) { + return talloc_strdup(NULL, a); + } + + if (unlikely(!a)) { + return s; + } + + slen = talloc_get_size(s); + if (likely(slen > 0)) { + slen--; + } + + return __talloc_strlendup_append(s, slen, a, strnlen(a, n)); +} + +#ifndef HAVE_VA_COPY +#ifdef HAVE___VA_COPY +#define va_copy(dest, src) __va_copy(dest, src) +#else +#define va_copy(dest, src) (dest) = (src) +#endif +#endif + +char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) +{ + int len; + char *ret; + va_list ap2; + char c; + + /* this call looks strange, but it makes it work on older solaris boxes */ + va_copy(ap2, ap); + len = vsnprintf(&c, 1, fmt, ap2); + va_end(ap2); + if (unlikely(len < 0)) { + return NULL; + } + + ret = (char *)__talloc(t, len+1); + if (unlikely(!ret)) return NULL; + + va_copy(ap2, ap); + vsnprintf(ret, len+1, fmt, ap2); + va_end(ap2); + + _talloc_set_name_const(ret, ret); + return ret; +} + + +/* + Perform string formatting, and return a pointer to newly allocated + memory holding the result, inside a memory pool. + */ +char *talloc_asprintf(const void *t, const char *fmt, ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = talloc_vasprintf(t, fmt, ap); + va_end(ap); + return ret; +} + +static inline char *__talloc_vaslenprintf_append(char *s, size_t slen, + const char *fmt, va_list ap) + PRINTF_ATTRIBUTE(3,0); + +static inline char *__talloc_vaslenprintf_append(char *s, size_t slen, + const char *fmt, va_list ap) +{ + ssize_t alen; + va_list ap2; + char c; + + va_copy(ap2, ap); + alen = vsnprintf(&c, 1, fmt, ap2); + va_end(ap2); + + if (alen <= 0) { + /* Either the vsnprintf failed or the format resulted in + * no characters being formatted. In the former case, we + * ought to return NULL, in the latter we ought to return + * the original string. Most current callers of this + * function expect it to never return NULL. + */ + return s; + } + + s = talloc_realloc(NULL, s, char, slen + alen + 1); + if (!s) return NULL; + + va_copy(ap2, ap); + vsnprintf(s + slen, alen + 1, fmt, ap2); + va_end(ap2); + + _talloc_set_name_const(s, s); + return s; +} + +/** + * Realloc @p s to append the formatted result of @p fmt and @p ap, + * and return @p s, which may have moved. Good for gradually + * accumulating output into a string buffer. Appends at the end + * of the string. + **/ +char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) +{ + if (unlikely(!s)) { + return talloc_vasprintf(NULL, fmt, ap); + } + + return __talloc_vaslenprintf_append(s, strlen(s), fmt, ap); +} + +/** + * Realloc @p s to append the formatted result of @p fmt and @p ap, + * and return @p s, which may have moved. Always appends at the + * end of the talloc'ed buffer, not the end of the string. + **/ +char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) +{ + size_t slen; + + if (unlikely(!s)) { + return talloc_vasprintf(NULL, fmt, ap); + } + + slen = talloc_get_size(s); + if (likely(slen > 0)) { + slen--; + } + + return __talloc_vaslenprintf_append(s, slen, fmt, ap); +} + +/* + Realloc @p s to append the formatted result of @p fmt and return @p + s, which may have moved. Good for gradually accumulating output + into a string buffer. + */ +char *talloc_asprintf_append(char *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + s = talloc_vasprintf_append(s, fmt, ap); + va_end(ap); + return s; +} + +/* + Realloc @p s to append the formatted result of @p fmt and return @p + s, which may have moved. Good for gradually accumulating output + into a buffer. + */ +char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + s = talloc_vasprintf_append_buffer(s, fmt, ap); + va_end(ap); + return s; +} + +/* + alloc an array, checking for integer overflow in the array size +*/ +void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_named_const(ctx, el_size * count, name); +} + +/* + alloc an zero array, checking for integer overflow in the array size +*/ +void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_zero(ctx, el_size * count, name); +} + +/* + realloc an array, checking for integer overflow in the array size +*/ +void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_realloc(ctx, ptr, el_size * count, name); +} + +/* + a function version of talloc_realloc(), so it can be passed as a function pointer + to libraries that want a realloc function (a realloc function encapsulates + all the basic capabilities of an allocation library, which is why this is useful) +*/ +void *talloc_realloc_fn(const void *context, void *ptr, size_t size) +{ + return _talloc_realloc(context, ptr, size, NULL); +} + + +static int talloc_autofree_destructor(void *ptr) +{ + autofree_context = NULL; + return 0; +} + +static void talloc_autofree(void) +{ + _talloc_free(autofree_context); +} + +/* + return a context which will be auto-freed on exit + this is useful for reducing the noise in leak reports +*/ +void *talloc_autofree_context(void) +{ + if (autofree_context == NULL) { + autofree_context = _talloc_named_const(NULL, 0, "autofree_context"); + talloc_set_destructor(autofree_context, talloc_autofree_destructor); + atexit(talloc_autofree); + } + return autofree_context; +} + +size_t talloc_get_size(const void *context) +{ + struct talloc_chunk *tc; + + if (context == NULL) + return 0; + + tc = talloc_chunk_from_ptr(context); + + return tc->size; +} + +/* + find a parent of this context that has the given name, if any +*/ +void *talloc_find_parent_byname(const void *context, const char *name) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + return NULL; + } + + tc = talloc_chunk_from_ptr(context); + while (tc) { + if (tc->name && strcmp(tc->name, name) == 0) { + return TC_PTR_FROM_CHUNK(tc); + } + while (tc && tc->prev) tc = tc->prev; + if (tc) { + tc = tc->parent; + } + } + return NULL; +} + +/* + show the parentage of a context +*/ +void talloc_show_parents(const void *context, FILE *file) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + fprintf(file, "talloc no parents for NULL\n"); + return; + } + + tc = talloc_chunk_from_ptr(context); + fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context)); + while (tc) { + fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc))); + while (tc && tc->prev) tc = tc->prev; + if (tc) { + tc = tc->parent; + } + } + fflush(file); +} + +/* + return 1 if ptr is a parent of context +*/ +int talloc_is_parent(const void *context, const void *ptr) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + return 0; + } + + tc = talloc_chunk_from_ptr(context); + while (tc) { + if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1; + while (tc && tc->prev) tc = tc->prev; + if (tc) { + tc = tc->parent; + } + } + return 0; +} diff --git a/src/host/libosmocom/src/timer.c b/src/host/libosmocom/src/timer.c new file mode 100644 index 00000000..acb4d923 --- /dev/null +++ b/src/host/libosmocom/src/timer.c @@ -0,0 +1,185 @@ +/* + * (C) 2008,2009 by Holger Hans Peter Freyther <zecke@selfish.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <assert.h> +#include <string.h> +#include <osmocom/timer.h> + +static LLIST_HEAD(timer_list); +static struct timeval s_nearest_time; +static struct timeval s_select_time; + +#define MICRO_SECONDS 1000000LL + +#define TIME_SMALLER(left, right) \ + (left.tv_sec*MICRO_SECONDS+left.tv_usec) <= (right.tv_sec*MICRO_SECONDS+right.tv_usec) + +void bsc_add_timer(struct timer_list *timer) +{ + struct timer_list *list_timer; + + /* TODO: Optimize and remember the closest item... */ + timer->active = 1; + + /* this might be called from within update_timers */ + llist_for_each_entry(list_timer, &timer_list, entry) + if (timer == list_timer) + return; + + timer->in_list = 1; + llist_add(&timer->entry, &timer_list); +} + +void bsc_schedule_timer(struct timer_list *timer, int seconds, int microseconds) +{ + struct timeval current_time; + + gettimeofday(¤t_time, NULL); + unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec; + currentTime += seconds * MICRO_SECONDS + microseconds; + timer->timeout.tv_sec = currentTime / MICRO_SECONDS; + timer->timeout.tv_usec = currentTime % MICRO_SECONDS; + bsc_add_timer(timer); +} + +void bsc_del_timer(struct timer_list *timer) +{ + if (timer->in_list) { + timer->active = 0; + timer->in_list = 0; + llist_del(&timer->entry); + } +} + +int bsc_timer_pending(struct timer_list *timer) +{ + return timer->active; +} + +/* + * if we have a nearest time return the delta between the current + * time and the time of the nearest timer. + * If the nearest timer timed out return NULL and then we will + * dispatch everything after the select + */ +struct timeval *bsc_nearest_timer() +{ + struct timeval current_time; + + if (s_nearest_time.tv_sec == 0 && s_nearest_time.tv_usec == 0) + return NULL; + + if (gettimeofday(¤t_time, NULL) == -1) + return NULL; + + unsigned long long nearestTime = s_nearest_time.tv_sec * MICRO_SECONDS + s_nearest_time.tv_usec; + unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec; + + if (nearestTime < currentTime) { + s_select_time.tv_sec = 0; + s_select_time.tv_usec = 0; + } else { + s_select_time.tv_sec = (nearestTime - currentTime) / MICRO_SECONDS; + s_select_time.tv_usec = (nearestTime - currentTime) % MICRO_SECONDS; + } + + return &s_select_time; +} + +/* + * Find the nearest time and update s_nearest_time + */ +void bsc_prepare_timers() +{ + struct timer_list *timer, *nearest_timer = NULL; + llist_for_each_entry(timer, &timer_list, entry) { + if (!nearest_timer || TIME_SMALLER(timer->timeout, nearest_timer->timeout)) { + nearest_timer = timer; + } + } + + if (nearest_timer) { + s_nearest_time = nearest_timer->timeout; + } else { + memset(&s_nearest_time, 0, sizeof(struct timeval)); + } +} + +/* + * fire all timers... and remove them + */ +int bsc_update_timers() +{ + struct timeval current_time; + struct timer_list *timer, *tmp; + int work = 0; + + gettimeofday(¤t_time, NULL); + + /* + * The callbacks might mess with our list and in this case + * even llist_for_each_entry_safe is not safe to use. To allow + * del_timer, add_timer, schedule_timer to be called from within + * the callback we jump through some loops. + * + * First we set the handled flag of each active timer to zero, + * then we iterate over the list and execute the callbacks. As the + * list might have been changed (specially the next) from within + * the callback we have to start over again. Once every callback + * is dispatched we will remove the non-active from the list. + * + * TODO: If this is a performance issue we can poison a global + * variable in add_timer and del_timer and only then restart. + */ + llist_for_each_entry(timer, &timer_list, entry) { + timer->handled = 0; + } + +restart: + llist_for_each_entry(timer, &timer_list, entry) { + if (!timer->handled && TIME_SMALLER(timer->timeout, current_time)) { + timer->handled = 1; + timer->active = 0; + (*timer->cb)(timer->data); + work = 1; + goto restart; + } + } + + llist_for_each_entry_safe(timer, tmp, &timer_list, entry) { + timer->handled = 0; + if (!timer->active) { + bsc_del_timer(timer); + } + } + + return work; +} + +int bsc_timer_check(void) +{ + struct timer_list *timer; + int i = 0; + + llist_for_each_entry(timer, &timer_list, entry) { + i++; + } + return i; +} |