diff options
author | Jeremy Rocher <jeremy.rocher@qorvo.com> | 2022-06-21 01:52:50 +0200 |
---|---|---|
committer | Victor Liu <victorliu@google.com> | 2022-07-12 23:08:55 +0000 |
commit | 88a70dac5c39e1c2c59bc9c36302619780f39fbf (patch) | |
tree | 2a73d846577da61d98ff86b22d94cc31b3df326a | |
parent | f744666b6e2e7b3602da2d0e262a10e8b183d552 (diff) | |
download | uwb-88a70dac5c39e1c2c59bc9c36302619780f39fbf.tar.gz |
Qorvo UWB Stack Release 06/21/2022 for QPR1
Features:
* Fira & vendor RSSI over UCI
* UCI RANGE_DATA_NTF_CONFIG support for proximity near/far
* UCI power stats query
* CCC/Fira interleaving
Note:
* Added __nocfi on llhw_set_hrp_uwb_params() to WA unexepected panic
due CFI check
Bug: 236612098
Change-Id: I98f2d147dff79e304f59aed3e85793c3512cc593
124 files changed, 9503 insertions, 5180 deletions
diff --git a/kernel/drivers/net/ieee802154/dw3000-overlay.dts b/kernel/drivers/net/ieee802154/dw3000-overlay.dts index 39cb0da..775913a 100644 --- a/kernel/drivers/net/ieee802154/dw3000-overlay.dts +++ b/kernel/drivers/net/ieee802154/dw3000-overlay.dts @@ -76,4 +76,3 @@ panid = <&dw3000>,"decawave,panid;0"; }; }; - diff --git a/kernel/drivers/net/ieee802154/dw3000.h b/kernel/drivers/net/ieee802154/dw3000.h index 5f03416..e21ef8e 100644 --- a/kernel/drivers/net/ieee802154/dw3000.h +++ b/kernel/drivers/net/ieee802154/dw3000.h @@ -191,6 +191,7 @@ enum dw3000_ciagdiag_reg_select { * @tx_fctrl: Transmit frame control * @rx_timeout_pac: Preamble detection timeout period in units of PAC size * symbols + * @rx_frame_timeout_dly: reception frame timeout period in units of dly. * @w4r_time: Wait-for-response time (RX after TX delay) * @sts_key: STS Key * @sts_iv: STS IV @@ -208,6 +209,7 @@ struct dw3000_local_data { u16 max_frames_len; s16 ststhreshold; u16 rx_timeout_pac; + u32 rx_frame_timeout_dly; u32 tx_fctrl; u32 w4r_time; u8 sts_key[AES_KEYSIZE_128]; @@ -217,12 +219,18 @@ struct dw3000_local_data { /* Statistics items */ enum dw3000_stats_items { DW3000_STATS_RX_GOOD, - DW3000_STATS_RX_TO, DW3000_STATS_RX_ERROR, + DW3000_STATS_RX_TO, __DW3000_STATS_COUNT }; -/* DW3000 statistics */ +/** + * struct dw3000_stats - DW3000 statistics + * @count: Count per-items + * @rssi: Last RSSI data per type + * @enabled: Stats enabled flag + * @indexes: Free-running index per-items + */ struct dw3000_stats { /* Total stats */ u16 count[__DW3000_STATS_COUNT]; @@ -230,6 +238,7 @@ struct dw3000_stats { struct dw3000_rssi rssi[DW3000_RSSI_REPORTS_MAX]; /* Stats on/off */ bool enabled; + u16 indexes[__DW3000_STATS_COUNT]; }; /* Maximum skb length @@ -436,6 +445,7 @@ enum config_changed_flags { * @cur_state: current state defined by enum power_state * @tx_adjust: TX time adjustment based on frame length * @rx_start: RX start date in DTU for RX time adjustment + * @interrupts: Hardware interrupts count on the device. */ struct dw3000_power { struct sysfs_power_stats stats[DW3000_PWR_MAX]; @@ -443,6 +453,7 @@ struct dw3000_power { int cur_state; int tx_adjust; u32 rx_start; + atomic64_t interrupts; }; /** @@ -451,8 +462,8 @@ struct dw3000_power { * @config_changed: bitfield of configuration changed during DEEP-SLEEP * @frame_idx: saved frame index to use for deferred TX/RX * @tx_skb: saved frame to transmit for deferred TX - * @tx_info: saved info to use for deferred TX - * @rx_info: saved parameter for deferred RX + * @tx_config: saved config to use for deferred TX + * @rx_config: saved parameter for deferred RX * @regbackup: registers backup to detect diff * @compare_work: deferred registers backup compare work */ @@ -462,8 +473,8 @@ struct dw3000_deep_sleep_state { int frame_idx; struct sk_buff *tx_skb; union { - struct mcps802154_tx_frame_info tx_info; - struct mcps802154_rx_info rx_info; + struct mcps802154_tx_frame_config tx_config; + struct mcps802154_rx_frame_config rx_config; }; #ifdef CONFIG_DW3000_DEBUG void *regbackup; @@ -496,6 +507,23 @@ struct dw3000_cir_data; #define DW3000_MAX_QUEUED_SPI_XFER 32 #define DW3000_QUEUED_SPI_BUFFER_SZ 2048 +/* Number of samples to average. */ +#define DW3000_NB_AVERAGE 1 + +/** + * struct dw3000_rx_ctx - Custom rx context use with rx_init/rx_get_measurement. + * @pdoa_rad_q11: Array of PDoA measurements. + * @aoa_rad_q11: Array of AoA measurements. + * @index: Index for pdoa_rad_q11 and aoa_rad_q11 arrays. + * @sample_valid_nb: Number of valid measurements in array, used for average. + */ +struct dw3000_rx_ctx { + int pdoa_rad_q11[DW3000_NB_AVERAGE]; + int aoa_rad_q11[DW3000_NB_AVERAGE]; + int index; + int sample_valid_nb; +}; + /** * struct dw3000 - main DW3000 device structure * @spi: pointer to corresponding spi device @@ -543,6 +571,7 @@ struct dw3000_cir_data; * @coex_interval_us: Minimum interval between two operations in us * under which WiFi coexistence GPIO is kept active * @coex_gpio: WiFi coexistence GPIO, >= 0 if activated + * @coex_enabled: WiFi coexistence activation * @coex_status: WiFi coexistence GPIO status, 1 if activated * @lna_pa_mode: LNA/PA configuration to use * @autoack: auto-ack status, true if activated @@ -557,6 +586,7 @@ struct dw3000_cir_data; * @restricted_channels: bit field of restricted channels * @tx_rf2: parameter to enable the tx on rf2 port * @cir_data_changed: true if buffer data have been reallocated + * @full_cia_read: CIA registers fully loaded into cir_data struct * @cir_data: allocated CIR exploitation data * @msg_queue: SPI message holding transfer queue * @msg_queue_xfer: next transfer available @@ -647,6 +677,7 @@ struct dw3000 { unsigned coex_margin_us; unsigned coex_interval_us; s8 coex_gpio; + bool coex_enabled; int coex_status; /* LNA/PA mode */ s8 lna_pa_mode; @@ -674,6 +705,7 @@ struct dw3000 { u8 tx_rf2; /* Channel impulse response data */ bool cir_data_changed; + bool full_cia_read; struct dw3000_cir_data *cir_data; /* SPI message holding transfers queue */ struct spi_message *msg_queue; diff --git a/kernel/drivers/net/ieee802154/dw3000_calib.c b/kernel/drivers/net/ieee802154/dw3000_calib.c index 31fc047..31bd4a7 100644 --- a/kernel/drivers/net/ieee802154/dw3000_calib.c +++ b/kernel/drivers/net/ieee802154/dw3000_calib.c @@ -26,7 +26,7 @@ /* clang-format off */ #define CHAN_PRF_PARAMS (4 * DW3000_CALIBRATION_PRF_MAX) #define ANT_CHAN_PARAMS (CHAN_PRF_PARAMS * DW3000_CALIBRATION_CHANNEL_MAX) -#define ANT_OTHER_PARAMS (3) /* port, selector_gpio... */ +#define ANT_OTHER_PARAMS (4) /* port, selector_gpio... */ #define ANTPAIR_CHAN_PARAMS (2 * DW3000_CALIBRATION_CHANNEL_MAX + 1) #define OTHER_PARAMS (13) /* xtal_trim, temperature_reference, @@ -38,7 +38,7 @@ #define MAX_CALIB_KEYS ((ANTMAX * (ANT_CHAN_PARAMS + ANT_OTHER_PARAMS)) + \ (ANTPAIR_MAX * ANTPAIR_CHAN_PARAMS) + \ - (DW3000_CALIBRATION_CHANNEL_MAX) + \ + (DW3000_CALIBRATION_CHANNEL_MAX * 2) + \ OTHER_PARAMS) #define DW_OFFSET(m) offsetof(struct dw3000, m) @@ -61,7 +61,8 @@ PRF_CAL_INFO(ant[x].ch[1], 1), \ CAL_INFO(ant[x].port), \ CAL_INFO(ant[x].selector_gpio), \ - CAL_INFO(ant[x].selector_gpio_value) + CAL_INFO(ant[x].selector_gpio_value), \ + CAL_INFO(ant[x].caps) #define ANTPAIR_CAL_INFO(x,y) \ CAL_INFO(antpair[ANTPAIR_IDX(x, y)].ch[0].pdoa_offset), \ @@ -90,7 +91,9 @@ static const struct { ANTPAIR_CAL_INFO(2,3), /* chY.* */ CAL_INFO(ch[0].pll_locking_code), + CAL_INFO(ch[0].wifi_coex_enabled), CAL_INFO(ch[1].pll_locking_code), + CAL_INFO(ch[1].wifi_coex_enabled), /* other with direct access in struct dw3000 */ DW_INFO(txconfig.smart), DW_INFO(auto_sleep_margin_us), @@ -122,7 +125,8 @@ static const struct { PRF_CAL_LABEL(x, 9, 64), \ "ant" #x ".port", \ "ant" #x ".selector_gpio", \ - "ant" #x ".selector_gpio_value" + "ant" #x ".selector_gpio_value", \ + "ant" #x ".caps" #define PDOA_CAL_LABEL(a, b, c) \ "ant" #a ".ant" #b ".ch" #c ".pdoa_offset", \ @@ -150,7 +154,9 @@ static const char *const dw3000_calib_keys[MAX_CALIB_KEYS + 1] = { ANTPAIR_CAL_LABEL(2,3), /* chY.* */ "ch5.pll_locking_code", + "ch5.wifi_coex-enabled", "ch9.pll_locking_code", + "ch9.wifi_coex-enabled", /* other */ "smart_tx_power", "auto_sleep_margin", @@ -323,6 +329,9 @@ int dw3000_calib_update_config(struct dw3000 *dw) /* Shortcut pointers to reduce line length. */ ant_calib_prf = &ant_calib->ch[chanidx].prf[prfidx]; + /* WiFi coexistence according to current channel */ + dw->coex_enabled = dw->calib_data.ch[chanidx].wifi_coex_enabled; + /* Update TX configuration */ txconfig->power = ant_calib_prf->tx_power ? ant_calib_prf->tx_power : 0xfefefefe; diff --git a/kernel/drivers/net/ieee802154/dw3000_calib.h b/kernel/drivers/net/ieee802154/dw3000_calib.h index c51ae17..b136b4c 100644 --- a/kernel/drivers/net/ieee802154/dw3000_calib.h +++ b/kernel/drivers/net/ieee802154/dw3000_calib.h @@ -82,10 +82,12 @@ extern const dw3000_pdoa_lut_t dw3000_default_lut_ch9; /** * struct dw3000_channel_calib - per-channel dependent calibration parameters * @pll_locking_code: PLL locking code + * @wifi_coex_enabled: WiFi coexistence activation */ struct dw3000_channel_calib { /* chY.pll_locking_code */ u32 pll_locking_code; + bool wifi_coex_enabled; }; /** @@ -109,6 +111,7 @@ struct dw3000_antenna_calib_prf { * @port: port value this antenna belong to (0 for RF1, 1 for RF2) * @selector_gpio: GPIO number to select this antenna * @selector_gpio_value: GPIO value to select this antenna + * @caps: antenna capabilities */ struct dw3000_antenna_calib { /* antX.chY.prfZ.* */ @@ -116,7 +119,7 @@ struct dw3000_antenna_calib { struct dw3000_antenna_calib_prf prf[DW3000_CALIBRATION_PRF_MAX]; } ch[DW3000_CALIBRATION_CHANNEL_MAX]; /* antX.* */ - u8 port, selector_gpio, selector_gpio_value; + u8 port, selector_gpio, selector_gpio_value, caps; }; /** diff --git a/kernel/drivers/net/ieee802154/dw3000_chip.h b/kernel/drivers/net/ieee802154/dw3000_chip.h index 136a2c9..5871ea0 100644 --- a/kernel/drivers/net/ieee802154/dw3000_chip.h +++ b/kernel/drivers/net/ieee802154/dw3000_chip.h @@ -25,6 +25,7 @@ /* Forward declaration */ struct dw3000; +struct dw3000_rssi; /** * enum dw3000_chip_register_flags - flags for the register declaration @@ -117,6 +118,7 @@ struct dw3000_chip_register_priv { * @kick_ops_table_on_wakeup: kick the desired operating parameter set table * @kick_dgc_on_wakeup: kick the DGC upon wakeup from sleep * @get_registers: Return known registers table and it's size + * @compute_rssi: Uses the parameters to compute RSSI of current frame */ struct dw3000_chip_ops { int (*softreset)(struct dw3000 *dw); @@ -137,6 +139,8 @@ struct dw3000_chip_ops { int (*kick_dgc_on_wakeup)(struct dw3000 *dw); const struct dw3000_chip_register *(*get_registers)(struct dw3000 *dw, size_t *count); + u32 (*compute_rssi)(struct dw3000 *dw, struct dw3000_rssi *rssi, + bool rx_tune, u8 sts); }; /** diff --git a/kernel/drivers/net/ieee802154/dw3000_chip_c0.c b/kernel/drivers/net/ieee802154/dw3000_chip_c0.c index 18033d6..2401504 100644 --- a/kernel/drivers/net/ieee802154/dw3000_chip_c0.c +++ b/kernel/drivers/net/ieee802154/dw3000_chip_c0.c @@ -344,6 +344,61 @@ static int dw3000_c0_kick_dgc_on_wakeup(struct dw3000 *dw) dgc_sel | DW3000_NVM_CFG_DGC_KICK_BIT_MASK); } +/** + * dw3000_c0_compute_rssi() - Compute RSSI from its composites + * @dw: the DW device + * @rssi: RSSI composites + * @rx_tune: state of RX_TUNE_EN bit leads to use dgc_dec value or not + * @sts: current sts mode + * + * Because RSSI cannot be positive in our case (would mean that signals have + * been amplified) we return an unsigned integer considered as always negative. + * + * Return: 0 on error, else the RSSI in absolute value and as an integer. + * expressed in dBm, Q32.0. + */ +static u32 dw3000_c0_compute_rssi(struct dw3000 *dw, struct dw3000_rssi *rssi, + bool rx_tune, u8 sts) +{ + /* Details are logged into UWB-3455 + * DW3700 User Manual v0.3 and 4 section 4.7.2 gives that RSSI + * can be computed using this formula: + * rssi = 10 * log10 ((cir_pwr * 2^21) / pacc_cnt ^ 2) + 6D - A(prf) + * But log10 isn't implemented ; let's use ilog2 instead, because it is + * easy to do on binary numbers. Thus, formula becomes: + * rssi = 3*log2(cir_pwr) - 6*log2(pacc_cnt) + 3*log2(2^21) + 6*D - A(prf) + * Notice that 3*log2(2^21) +6*D - A(prf) can be pre-computed. + * Factor 3 comes from ln(2) / ln(10) almost equal to 3 / 10 ; this is an + * approximation done in equation turning log10 into log2 to avoid floating point + */ + + /* u32 is used because ilog2 macro cannot work on bitfield */ + u32 pwr = rssi->cir_pwr; + u32 cnt = rssi->pacc_cnt; + s32 r, rssi_constant; + + /* Do not consider bad packets */ + if (unlikely(!pwr || !cnt)) + return 0; + + rssi_constant = DW3000_RSSI_CONSTANT + 6 * rx_tune * rssi->dgc_dec; + + if (!rssi->prf_64mhz) { + rssi_constant -= DW3000_RSSI_OFFSET_PRF16; + } else + rssi_constant -= ((sts == DW3000_STS_MODE_OFF) ? + DW3000_RSSI_OFFSET_PRF64_IPATOV : + DW3000_RSSI_OFFSET_PRF64_STS); + + r = 3 * ilog2(pwr) - 6 * ilog2(cnt) + rssi_constant; + if (unlikely(r > 0)) { + dev_err(dw->dev, "bad rssi value. Forced to 0\n"); + r = 0; + } + + return (u32)-r; +} + const struct dw3000_chip_ops dw3000_chip_c0_ops = { .softreset = dw3000_c0_softreset, .init = dw3000_c0_init, @@ -360,4 +415,5 @@ const struct dw3000_chip_ops dw3000_chip_c0_ops = { .kick_ops_table_on_wakeup = dw3000_c0_kick_ops_table_on_wakeup, .kick_dgc_on_wakeup = dw3000_c0_kick_dgc_on_wakeup, .get_registers = dw3000_c0_get_registers, + .compute_rssi = dw3000_c0_compute_rssi, }; diff --git a/kernel/drivers/net/ieee802154/dw3000_chip_c0.h b/kernel/drivers/net/ieee802154/dw3000_chip_c0.h index cc784e7..25734f9 100644 --- a/kernel/drivers/net/ieee802154/dw3000_chip_c0.h +++ b/kernel/drivers/net/ieee802154/dw3000_chip_c0.h @@ -38,4 +38,11 @@ * when a calibration from scratch is executed */ #define DW3000_C0_PLL_CALIBRATION_FROM_SCRATCH_DELAY_US (400) +/* RSSI constants */ +#define DW3000_RSSI_OFFSET_PRF64_STS 121 +#define DW3000_RSSI_OFFSET_PRF64_IPATOV 122 +#define DW3000_RSSI_OFFSET_PRF16 114 +#define DW3000_RSSI_CONSTANT \ + 63 /* 3 * log2(2^21) because log2 used instead of log10 */ + #endif /* __DW3000_CHIP_C0_H */ diff --git a/kernel/drivers/net/ieee802154/dw3000_chip_d0.c b/kernel/drivers/net/ieee802154/dw3000_chip_d0.c index 10e9643..8bf3fb8 100644 --- a/kernel/drivers/net/ieee802154/dw3000_chip_d0.c +++ b/kernel/drivers/net/ieee802154/dw3000_chip_d0.c @@ -259,6 +259,61 @@ static int dw3000_d0_pll_calibration_from_scratch(struct dw3000 *dw) return rc; } +/** + * dw3000_d0_compute_rssi() - Compute RSSI from its composites + * @dw: the DW device + * @rssi: RSSI composites + * @rx_tune: state of RX_TUNE_EN bit leads to use dgc_dec value or not + * @sts: current sts mode + * + * Because RSSI cannot be positive in our case (would mean that signals have + * been amplified) we return an unsigned integer considered as always negative. + * + * Return: 0 on error, else the RSSI in absolute value and as an integer. + * expressed in dBm, Q32.0. + */ +u32 dw3000_d0_compute_rssi(struct dw3000 *dw, struct dw3000_rssi *rssi, + bool rx_tune, u8 sts) +{ + /* Details are logged into UWB-3455 + * DW3700 User Manual v0.4 section 4.7.2 gives that RSSI + * can be computed using this formula: + * rssi = 10 * log10 ((cir_pwr * 2^17) / pacc_cnt ^ 2) + 6D - A(prf) + * But log10 isn't implemented ; let's use ilog2 instead, because it is + * easy to do on binary numbers. Thus, formula becomes: + * rssi = 3*log2(cir_pwr) - 6*log2(pacc_cnt) + 3*log2(2^17) + 6*D - A(prf) + * Notice that 3*log2(2^17) +6*D - A(prf) can be pre-computed. + * Factor 3 comes from ln(2) / ln(10) almost equal to 3 / 10 ; this is an + * approximation done in equation turning log10 into log2 to avoid floating point + */ + + /* u32 is used because ilog2 macro cannot work on bitfield */ + u32 pwr = rssi->cir_pwr; + u32 cnt = rssi->pacc_cnt; + s32 r, rssi_constant; + + /* Do not consider bad packets */ + if (unlikely(!pwr || !cnt)) + return 0; + + rssi_constant = DW3000_RSSI_CONSTANT + 6 * rx_tune * rssi->dgc_dec; + + if (!rssi->prf_64mhz) { + rssi_constant -= DW3000_RSSI_OFFSET_PRF16; + } else + rssi_constant -= ((sts == DW3000_STS_MODE_OFF) ? + DW3000_RSSI_OFFSET_PRF64_IPATOV : + DW3000_RSSI_OFFSET_PRF64_STS); + + r = 3 * ilog2(pwr) - 6 * ilog2(cnt) + rssi_constant; + if (unlikely(r > 0)) { + dev_err(dw->dev, "bad rssi value. Forced to 0\n"); + r = 0; + } + + return (u32)-r; +} + const struct dw3000_chip_ops dw3000_chip_d0_ops = { .softreset = dw3000_d0_softreset, .init = dw3000_d0_init, @@ -272,4 +327,5 @@ const struct dw3000_chip_ops dw3000_chip_d0_ops = { .prog_pll_coarse_code = dw3000_c0_prog_pll_coarse_code, .set_mrxlut = dw3000_c0_set_mrxlut, .get_registers = dw3000_d0_get_registers, + .compute_rssi = dw3000_d0_compute_rssi, }; diff --git a/kernel/drivers/net/ieee802154/dw3000_chip_d0.h b/kernel/drivers/net/ieee802154/dw3000_chip_d0.h index ac4d22e..4804623 100644 --- a/kernel/drivers/net/ieee802154/dw3000_chip_d0.h +++ b/kernel/drivers/net/ieee802154/dw3000_chip_d0.h @@ -38,4 +38,11 @@ * when a calibration from scratch is executed */ #define DW3000_D0_PLL_CALIBRATION_FROM_SCRATCH_DELAY_US (400) +/* RSSI constants */ +#define DW3000_RSSI_OFFSET_PRF64_STS 121 +#define DW3000_RSSI_OFFSET_PRF64_IPATOV 122 +#define DW3000_RSSI_OFFSET_PRF16 114 +#define DW3000_RSSI_CONSTANT \ + 51 /* 3 * log2(2^17) because log2 used instead of log10 */ + #endif /* __DW3000_CHIP_D0_H */ diff --git a/kernel/drivers/net/ieee802154/dw3000_chip_e0.c b/kernel/drivers/net/ieee802154/dw3000_chip_e0.c index ff2079a..1605bf0 100644 --- a/kernel/drivers/net/ieee802154/dw3000_chip_e0.c +++ b/kernel/drivers/net/ieee802154/dw3000_chip_e0.c @@ -32,6 +32,8 @@ int dw3000_d0_init(struct dw3000 *dw); int dw3000_d0_coex_init(struct dw3000 *dw); const struct dw3000_chip_register *dw3000_d0_get_registers(struct dw3000 *dw, size_t *count); +u32 dw3000_d0_compute_rssi(struct dw3000 *dw, struct dw3000_rssi *rssi, + bool rx_tune, u8 sts); const u32 *dw3000_e0_get_config_mrxlut_chan(struct dw3000 *dw, u8 channel) { @@ -706,4 +708,5 @@ const struct dw3000_chip_ops dw3000_chip_e0_ops = { .pll_coarse_code = dw3000_e0_pll_coarse_code, .set_mrxlut = dw3000_e0_set_mrxlut, .get_registers = dw3000_d0_get_registers, + .compute_rssi = dw3000_d0_compute_rssi, }; diff --git a/kernel/drivers/net/ieee802154/dw3000_cir.h b/kernel/drivers/net/ieee802154/dw3000_cir.h index 9a87ffc..d66429c 100644 --- a/kernel/drivers/net/ieee802154/dw3000_cir.h +++ b/kernel/drivers/net/ieee802154/dw3000_cir.h @@ -54,7 +54,7 @@ struct dw3000_cir_record { * @fp_power3: first path power component f3 * @offset: user defined offset * @fp_index: index of first path record in CIR register - * @tdoa: tdoa raw value + * @pdoa: pdoa raw value * @acc: number of symbols accumulated in CIR * @type: CIR type field * @dummy: store the dummy byte firstly received at each CIR memory reading @@ -73,7 +73,7 @@ struct dw3000_cir_data { u32 fp_power3; s32 offset; u16 fp_index; - s16 tdoa; + u16 pdoa; u16 acc; u8 type; u8 dummy; diff --git a/kernel/drivers/net/ieee802154/dw3000_coex.h b/kernel/drivers/net/ieee802154/dw3000_coex.h index 2f39dc6..c2dc6b7 100644 --- a/kernel/drivers/net/ieee802154/dw3000_coex.h +++ b/kernel/drivers/net/ieee802154/dw3000_coex.h @@ -76,7 +76,7 @@ static inline int dw3000_coex_start(struct dw3000 *dw, bool *trx_delayed, int delay_us; int timer_us = 0; - if (dw->coex_gpio < 0) + if (dw->coex_gpio < 0 || !dw->coex_enabled) return 0; /* Add a margin for required SPI transactions to the coex delay time * to ensure GPIO change at right time. */ @@ -120,7 +120,7 @@ static inline int dw3000_coex_start(struct dw3000 *dw, bool *trx_delayed, */ static inline int dw3000_coex_stop(struct dw3000 *dw) { - if (dw->coex_gpio < 0) + if (dw->coex_gpio < 0 || !dw->coex_enabled) return 0; trace_dw3000_coex_gpio_stop(dw, dw->coex_status); diff --git a/kernel/drivers/net/ieee802154/dw3000_core.c b/kernel/drivers/net/ieee802154/dw3000_core.c index 6905c3c..a5b587c 100644 --- a/kernel/drivers/net/ieee802154/dw3000_core.c +++ b/kernel/drivers/net/ieee802154/dw3000_core.c @@ -26,6 +26,7 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/bitfield.h> +#include <linux/log2.h> #include "dw3000.h" #include "dw3000_core.h" @@ -1602,51 +1603,62 @@ MODULE_PARM_DESC(stats_enabled, /** * dw3000_rx_store_rssi() - Get RSSI data from last good RX frame * @dw: the DW device + * @rssi: points to current rssi record in stats structure + * @pkt_sts: sts mode of current packet * * The RSSI data must be read before incrementing counter. * It requires at least one received frame to get any data from * register. Both 'cir_pwr' and 'pacc_cnt' values cannot be null - * regarding the RSSI formula in the DW3700 User Manual v0.1 section 4.6.2. + * regarding the RSSI formula in the DW3700 User Manual v0.3 section 4.7.2. * * Return: 0 on success, else a negative error code. */ -static int dw3000_rx_store_rssi(struct dw3000 *dw) +int dw3000_rx_store_rssi(struct dw3000 *dw, struct dw3000_rssi *rssi, + u8 pkt_sts) { struct dw3000_config *config = &dw->config; - struct dw3000_stats *stats = &dw->stats; /* Only read RSSI after good RX frame */ - int idx = stats->count[DW3000_STATS_RX_GOOD] - 1; - bool sts = _dw3000_sts_is_enabled(dw); - const char *chip_name = dw3000_get_chip_name(dw); - struct dw3000_rssi *rssi; int rc; u32 cir_pwr; u16 pacc_cnt; u8 dgc_dec; + u32 diag1_addr; /* No data available */ - if (idx < 0) - return -EAGAIN; - /* Get RSSI data pointer to store data */ - rssi = &stats->rssi[idx]; - /* Read CIR power value */ - rc = dw3000_reg_read32( - dw, _ciadiag_reg_info[dw->data.ciadiag_reg_select].diag1, 0, - &cir_pwr); - if (unlikely(rc)) - return rc; - /* Read preamble accumulation count value */ - rc = dw3000_reg_read16( - dw, _ciadiag_reg_info[dw->data.ciadiag_reg_select].diag12, 0, - &pacc_cnt); - if (unlikely(rc)) - return rc; - /* Avoid "nan" and "-inf" in userspace when calculating average RSSI */ - if (cir_pwr == 0 || pacc_cnt == 0) { - dev_err(dw->dev, - "one or both values from CIA registers are null\n"); + if (!rssi) return -EAGAIN; + /* Get required data to calculate RSSI */ + if (dw->full_cia_read) { + /* Get values from already retrieved CIA registers */ + diag1_addr = (pkt_sts == DW3000_STS_MODE_OFF) ? + (DW3000_DB_DIAG_IP_DIAG1 >> 2) : + (DW3000_DB_DIAG_STS_DIAG1 >> 2); + cir_pwr = dw->cir_data->ciaregs[diag1_addr]; + pacc_cnt = dw->cir_data->acc; + } else { + /* Read CIR power value */ + rc = dw3000_reg_read32( + dw, + _ciadiag_reg_info[dw->data.ciadiag_reg_select].diag1, 0, + &cir_pwr); + if (unlikely(rc)) + return rc; + /* Read preamble accumulation count value */ + rc = dw3000_reg_read16( + dw, + _ciadiag_reg_info[dw->data.ciadiag_reg_select].diag12, + 0, &pacc_cnt); + if (unlikely(rc)) + return rc; + /* Reset minidiag to allow a new measure */ + rc = dw3000_reg_modify32(dw, DW3000_CIA_CONF_ID, 0, + ~DW3000_CIA_CONF_MINDIAG_BIT_MASK, + 0x0); + if (unlikely(rc)) + return rc; } + + /* Store in provided buffer */ rssi->cir_pwr = cir_pwr; rssi->pacc_cnt = pacc_cnt; rssi->prf_64mhz = ((config->rxCode >= 9) && (config->rxCode <= 24)); @@ -1657,31 +1669,88 @@ static int dw3000_rx_store_rssi(struct dw3000 *dw) return rc; rssi->dgc_dec = dgc_dec; - trace_dw3000_rx_rssi(dw, chip_name, sts, rssi->cir_pwr, rssi->pacc_cnt, - rssi->prf_64mhz, rssi->dgc_dec); return 0; } /** - * dw3000_rx_stats_inc() - Increment statistics - * @dw: the DW device - * @item: statistics item + * dw3000_rx_stats_inc() - Increment statistics. + * @dw: The DW device. + * @item: Statistics item. + * @rssi: The RSSI information to store. + * + * Return: 0 on success, else a negative error code. + */ +int dw3000_rx_stats_inc(struct dw3000 *dw, const enum dw3000_stats_items item, + struct dw3000_rssi *rssi) +{ + struct dw3000_stats *stats = &dw->stats; + const char *chip_name = dw3000_get_chip_name(dw); + bool sts_enabled = _dw3000_sts_is_enabled(dw); + u16 idx; + + /* Increment per-item counter */ + stats->count[item]++; + + /* Save provided RSSI data */ + if (!rssi) + return 0; + + /* Retrieve current idx */ + idx = stats->indexes[item]; + /* RSSI array is divided in two parts, RX_GOOD at first */ + idx += (DW3000_RSSI_REPORTS_MAX >> 1) * (item == DW3000_STATS_RX_ERROR); + stats->rssi[idx] = *rssi; + + /* Update where to store next RSSI */ + stats->indexes[item]++; + if (stats->indexes[item] >= (DW3000_RSSI_REPORTS_MAX >> 1)) + stats->indexes[item] = 0; + /* TODO(UWB-3455): Shall we reset count[item] too to maintain current behaviour? + * See also do_tm_cmd_get_rx_diag() function. + */ + + /* Trace RSSI data*/ + trace_dw3000_rx_rssi(dw, chip_name, sts_enabled, rssi->cir_pwr, + rssi->pacc_cnt, rssi->prf_64mhz, rssi->dgc_dec); + return 0; +} + +/** + * dw3000_rx_calc_rssi() - RSSI computation. + * @dw: The DW device. + * @rssi: The RSSI related informations. + * @info: the MCPS information structure to fill with RSSI. + * @sts: Current STS mode. + * + * This function checks if RSSI is required: by explicit MAC request or by + * stats, then retrieve, store and output it by tracepoint + * or in a structure field. * * Return: 0 on success, else a negative error code. */ -static inline int dw3000_rx_stats_inc(struct dw3000 *dw, - const enum dw3000_stats_items item) +int dw3000_rx_calc_rssi(struct dw3000 *dw, struct dw3000_rssi *rssi, + struct mcps802154_rx_frame_info *info, u8 sts) { + bool rssi_required = info->flags & MCPS802154_RX_FRAME_INFO_RSSI; int rc = 0; - if (dw->stats.enabled) { - if (dw->stats.count[item] >= DW3000_RSSI_REPORTS_MAX) - dw->stats.count[item] = 0; - dw->stats.count[item]++; - if (item == DW3000_STATS_RX_GOOD) { - rc = dw3000_rx_store_rssi(dw); - } - } - return rc; + bool rx_tune; + u8 reg; + + if (!rssi_required) + return 0; + + /* Get RX_TUNE_EN bit required by the RSSI formula */ + /* TODO: move this in dw3000_configure_dgc() to cache this value in + struct dw3000_local_data and avoid this read. */ + rc = dw3000_reg_read8(dw, DW3000_DGC_CFG_ID, 0, ®); + if (rc) + return rc; + rx_tune = reg & DW3000_DGC_CFG_RX_TUNE_EN_BIT_MASK; + /* Compute RSSI and give the result to the upper layer in Q7.1 */ + info->rssi = dw->chip_ops->compute_rssi(dw, rssi, rx_tune, sts) << 1; + if (!info->rssi) + info->flags &= ~MCPS802154_RX_FRAME_INFO_RSSI; + return 0; } static int dw3000_power_supply_one(struct regulator *regulator, bool onoff) @@ -1962,6 +2031,37 @@ static inline int dw3000_setpreambledetecttimeout(struct dw3000 *dw, return 0; } +/** + * dw3000_setrxtimeout() - Set the reception timeout + * @dw: the DW device + * @timeout_dly: reception total time timeout in dly unit. + * + * timeout value 0 will disable the timeout. + * + * Return: zero on success, else a negative error code. + */ +static inline int dw3000_setrxtimeout(struct dw3000 *dw, u32 timeout_dly) +{ + struct dw3000_local_data *local = &dw->data; + int rc; + if (local->rx_frame_timeout_dly == timeout_dly) + return 0; + if (timeout_dly) { + rc = dw3000_reg_write32(dw, DW3000_RX_FWTO_ID, 0, timeout_dly); + if (unlikely(rc)) + return rc; + rc = dw3000_reg_or16(dw, DW3000_SYS_CFG_ID, 0, + DW3000_SYS_CFG_RXWTOE_BIT_MASK); + } else { + rc = dw3000_reg_and16(dw, DW3000_SYS_CFG_ID, 0, + (u16)~DW3000_SYS_CFG_RXWTOE_BIT_MASK); + } + if (unlikely(rc)) + return rc; + local->rx_frame_timeout_dly = timeout_dly; + return 0; +} + static inline int dw3000_setdelayedtrxtime(struct dw3000 *dw, u32 starttime) { u32 sys_starttime = dw3000_dtu_to_sys_time(dw, starttime); @@ -2052,6 +2152,7 @@ static int do_wakeup(struct dw3000 *dw, const void *in, void *out) int dw3000_deepsleep_wakeup_now(struct dw3000 *dw, dw3000_idle_timeout_cb idle_timeout_cb, + u32 timestamp_dtu, enum operational_state next_operational_state) { struct dw3000_deep_sleep_state *dss = &dw->deep_sleep_state; @@ -2063,6 +2164,7 @@ int dw3000_deepsleep_wakeup_now(struct dw3000 *dw, dss->next_operational_state = next_operational_state; dw->idle_timeout_cb = idle_timeout_cb; + dw->idle_timeout_dtu = timestamp_dtu; return 0; } @@ -2155,6 +2257,7 @@ int dw3000_idle_cancel_timer(struct dw3000 *dw) return -ENOENT; /* Ensure wakeup ISR don't call the mcps802154_timer_expired() */ dw->idle_timeout_cb = NULL; + dw->idle_timeout = false; return 0; } @@ -2239,7 +2342,8 @@ int dw3000_check_operational_state(struct dw3000 *dw, int delay_dtu, rc = dw3000_wakeup(dw); if (unlikely(rc)) return rc; - /* fallthrough */ + /* Use kernel defined statement which exist since kernel 5.4. */ + fallthrough; case DW3000_OP_STATE_WAKE_UP: /* Inform caller to save parameters. Stored operation will redo deep sleep if needed. */ @@ -2410,7 +2514,7 @@ stop_coex: /** * dw3000_do_rx_enable() - handle RX enable MCPS operation * @dw: the DW device to put in RX mode - * @info: RX enable parameters from MCPS + * @config: RX enable parameters from MCPS * @frame_idx: Frame index in a continuous block * * This function is called to execute all required operation to enable RX on @@ -2428,13 +2532,15 @@ stop_coex: * Return: 0 on success, else a negative error code. */ int dw3000_do_rx_enable(struct dw3000 *dw, - const struct mcps802154_rx_info *info, int frame_idx) + const struct mcps802154_rx_frame_config *config, + int frame_idx) { struct dw3000_deep_sleep_state *dss = &dw->deep_sleep_state; struct mcps802154_llhw *llhw = dw->llhw; u32 cur_time_dtu = 0; u32 rx_date_dtu = 0; u32 timeout_pac = 0; + u32 frame_timeout_dly = 0; bool rx_delayed = true; int delay_dtu = 0; bool can_sync = false; @@ -2442,27 +2548,19 @@ int dw3000_do_rx_enable(struct dw3000 *dw, bool pdoa_enabled; u8 sts_mode; - trace_dw3000_mcps_rx_enable(dw, info->flags, info->timeout_dtu); + trace_dw3000_mcps_rx_enable(dw, config->flags, config->timeout_dtu); /* Ensure CFO is checked if responder wait first frame of round. */ - dw->data.check_cfo = !!(info->flags & MCPS802154_RX_INFO_RANGING_ROUND); + dw->data.check_cfo = + !!(config->flags & MCPS802154_RX_FRAME_CONFIG_RANGING_ROUND); /* Calculate the transfer date. */ - if (info->flags & MCPS802154_RX_INFO_TIMESTAMP_DTU) { + if (config->flags & MCPS802154_RX_FRAME_CONFIG_TIMESTAMP_DTU) { rx_date_dtu = - info->timestamp_dtu - DW3000_RX_ENABLE_STARTUP_DTU; + config->timestamp_dtu - DW3000_RX_ENABLE_STARTUP_DTU; } else { /* Receive immediately. */ rx_delayed = false; } - /* Calculate the timeout. */ - if (info->timeout_dtu == 0) { - timeout_pac = dw->pre_timeout_pac; - } else if (info->timeout_dtu != -1) { - timeout_pac = dw->pre_timeout_pac + - dtu_to_pac(llhw, info->timeout_dtu); - } else { - /* No timeout. */ - } if (rx_delayed) { cur_time_dtu = dw3000_get_dtu_time(dw); @@ -2491,39 +2589,62 @@ int dw3000_do_rx_enable(struct dw3000 *dw, dw->wakeup_done_cb = dw3000_wakeup_done_to_rx; dss->next_operational_state = DW3000_OP_STATE_RX; - dss->rx_info = *info; + dss->rx_config = *config; dss->frame_idx = frame_idx; return 0; } /* All operation below require the DW chip is in IDLE_PLL state */ + /* Calculate the preamble timeout. */ + if (config->timeout_dtu == 0) { + timeout_pac = dw->pre_timeout_pac; + } else if (config->timeout_dtu != -1) { + timeout_pac = dw->pre_timeout_pac + + dtu_to_pac(llhw, config->timeout_dtu); + } else { + /* No timeout. */ + } + + /* Add the frame timeout if needed. */ + if (timeout_pac && config->frame_timeout_dtu) { + frame_timeout_dly = + dtu_to_dly(llhw, config->frame_timeout_dtu) + + pac_to_dly(llhw, timeout_pac); + } + /* Start SPI queuing mode to minimise number of SPI messages Queue will be flushed by dw3000_tx_frame() itself. */ dw3000_spi_queue_start(dw); /* Enable STS */ - pdoa_enabled = !!(info->flags & MCPS802154_RX_INFO_RANGING_PDOA); - sts_mode = FIELD_GET(MCPS802154_RX_INFO_STS_MODE_MASK, info->flags); + pdoa_enabled = + !!(config->flags & MCPS802154_RX_FRAME_CONFIG_RANGING_PDOA); + sts_mode = FIELD_GET(MCPS802154_RX_FRAME_CONFIG_STS_MODE_MASK, + config->flags); rc = dw3000_set_sts_pdoa(dw, sts_mode, sts_to_pdoa(sts_mode, pdoa_enabled)); if (unlikely(rc)) goto fail; /* Ensure correct RX antennas are selected. */ - rc = dw3000_set_rx_antennas(dw, info->ant_set_id, pdoa_enabled); + rc = dw3000_set_rx_antennas(dw, config->ant_set_id, pdoa_enabled); if (unlikely(rc)) goto fail; - if (info->flags & MCPS802154_RX_INFO_AACK) { + if (config->flags & MCPS802154_RX_FRAME_CONFIG_AACK) { dw3000_enable_autoack(dw, false); } else { dw3000_disable_autoack(dw, false); } + rc = dw3000_setrxtimeout(dw, frame_timeout_dly); + if (unlikely(rc)) + goto fail; rc = dw3000_rx_enable(dw, rx_delayed, rx_date_dtu, timeout_pac); if (unlikely(rc)) goto fail; /* Store ranging clock requirement for next operation */ dw->need_ranging_clock = - (info->flags & MCPS802154_RX_INFO_KEEP_RANGING_CLOCK) != 0; + (config->flags & + MCPS802154_RX_FRAME_CONFIG_KEEP_RANGING_CLOCK) != 0; fail: if (rc) @@ -2537,6 +2658,7 @@ static irqreturn_t dw3000_irq_handler(int irq, void *context) { struct dw3000 *dw = context; + atomic64_inc(&dw->power.interrupts); dw3000_enqueue_irq(dw); return IRQ_HANDLED; @@ -3323,7 +3445,8 @@ int dw3000_tx_frame(struct dw3000 *dw, struct sk_buff *skb, bool tx_delayed, } if (skb) { - len = skb->len + IEEE802154_FCS_LEN; + /* FCS is already included while pctt is enabled */ + len = skb->len + (dw->pctt.enabled ? 0 : IEEE802154_FCS_LEN); /* Write frame properties to the transmit frame control register */ if (WARN_ON(len > dw->data.max_frames_len)) return -EINVAL; @@ -3364,8 +3487,7 @@ int dw3000_tx_frame(struct dw3000 *dw, struct sk_buff *skb, bool tx_delayed, if (unlikely(rc)) goto stop_coex; /* W4R mode are handled by TX event IRQ handler */ - dw3000_power_stats(dw, DW3000_PWR_TX, - skb ? skb->len + IEEE802154_FCS_LEN : 0); + dw3000_power_stats(dw, DW3000_PWR_TX, len); return 0; } @@ -3386,8 +3508,7 @@ int dw3000_tx_frame(struct dw3000 *dw, struct sk_buff *skb, bool tx_delayed, goto stop_coex; /* W4R mode are handled by TX event IRQ handler */ - dw3000_power_stats(dw, DW3000_PWR_TX, - skb ? skb->len + IEEE802154_FCS_LEN : 0); + dw3000_power_stats(dw, DW3000_PWR_TX, len); /* Check if late */ rc = dw3000_check_hpdwarn(dw); if (unlikely(rc)) { @@ -3407,7 +3528,7 @@ stop_coex: /** * dw3000_do_tx_frame() - handle TX frame MCPS operation * @dw: the device on which transmit frame - * @info: TX parameters from MCPS + * @config: TX parameters from MCPS * @skb: the frame to transmit * @frame_idx: Frame index in a continuous block * @@ -3427,7 +3548,7 @@ stop_coex: * Return: 0 on success, else a negative error code. */ int dw3000_do_tx_frame(struct dw3000 *dw, - const struct mcps802154_tx_frame_info *info, + const struct mcps802154_tx_frame_config *config, struct sk_buff *skb, int frame_idx) { struct dw3000_deep_sleep_state *dss = &dw->deep_sleep_state; @@ -3443,16 +3564,16 @@ int dw3000_do_tx_frame(struct dw3000 *dw, int rc; u8 sts_mode; - trace_dw3000_mcps_tx_frame(dw, info->flags, skb ? skb->len : 0); + trace_dw3000_mcps_tx_frame(dw, config->flags, skb ? skb->len : 0); /* Calculate the transfer date.*/ - if (info->flags & MCPS802154_TX_FRAME_TIMESTAMP_DTU) { - tx_date_dtu = info->timestamp_dtu + llhw->shr_dtu; + if (config->flags & MCPS802154_TX_FRAME_CONFIG_TIMESTAMP_DTU) { + tx_date_dtu = config->timestamp_dtu + llhw->shr_dtu; } else { /* Send immediately. */ tx_delayed = false; } - if (info->flags & MCPS802154_TX_FRAME_RANGING) + if (config->flags & MCPS802154_TX_FRAME_CONFIG_RANGING) ranging = true; if (tx_delayed) { @@ -3482,7 +3603,7 @@ int dw3000_do_tx_frame(struct dw3000 *dw, wakeup later */ dw->wakeup_done_cb = dw3000_wakeup_done_to_tx; dss->next_operational_state = DW3000_OP_STATE_TX; - dss->tx_info = *info; + dss->tx_config = *config; dss->tx_skb = skb; dss->frame_idx = frame_idx; return 0; @@ -3493,35 +3614,48 @@ int dw3000_do_tx_frame(struct dw3000 *dw, Queue will be flushed by dw3000_tx_frame() itself. */ dw3000_spi_queue_start(dw); + /* Oscillate XTAL around calibrated value to maximise successful PDoA probability */ + if (DW3000_XTAL_BIAS && + (config->flags & MCPS802154_TX_FRAME_CONFIG_RANGING_ROUND)) { + dw->data.xtal_bias = (dw->data.xtal_bias > 0 ? + -DW3000_XTAL_BIAS : + DW3000_XTAL_BIAS); + rc = dw3000_prog_xtrim(dw); + if (unlikely(rc)) + goto fail; + } /* Enable STS */ - sts_mode = FIELD_GET(MCPS802154_TX_FRAME_STS_MODE_MASK, info->flags); + sts_mode = FIELD_GET(MCPS802154_TX_FRAME_CONFIG_STS_MODE_MASK, + config->flags); rc = dw3000_set_sts_pdoa( dw, sts_mode, sts_to_pdoa(sts_mode, - info->flags & MCPS802154_TX_FRAME_RANGING_PDOA)); + config->flags & + MCPS802154_TX_FRAME_CONFIG_RANGING_PDOA)); if (unlikely(rc)) goto fail; /* Ensure correct TX antenna is selected. */ - rc = dw3000_set_tx_antenna(dw, info->ant_set_id); + rc = dw3000_set_tx_antenna(dw, config->ant_set_id); if (unlikely(rc)) goto fail; - if (info->rx_enable_after_tx_dtu > 0) { + if (config->rx_enable_after_tx_dtu > 0) { /* Disable auto-ack if it was previously enabled. */ dw3000_disable_autoack(dw, false); /* Calculate the after tx rx delay. */ - rx_delay_dly = dtu_to_dly(llhw, info->rx_enable_after_tx_dtu) - - DW3000_RX_ENABLE_STARTUP_DLY; + rx_delay_dly = + dtu_to_dly(llhw, config->rx_enable_after_tx_dtu) - + DW3000_RX_ENABLE_STARTUP_DLY; rx_delay_dly = rx_delay_dly >= 0 ? rx_delay_dly : 0; /* Calculate the after tx rx timeout. */ - if (info->rx_enable_after_tx_timeout_dtu == 0) { + if (config->rx_enable_after_tx_timeout_dtu == 0) { rx_timeout_pac = dw->pre_timeout_pac; - } else if (info->rx_enable_after_tx_timeout_dtu != -1) { + } else if (config->rx_enable_after_tx_timeout_dtu != -1) { rx_timeout_pac = dw->pre_timeout_pac + dtu_to_pac( llhw, - info->rx_enable_after_tx_timeout_dtu); + config->rx_enable_after_tx_timeout_dtu); } else { /* No timeout. */ } @@ -3533,7 +3667,8 @@ int dw3000_do_tx_frame(struct dw3000 *dw, /* Store ranging clock requirement for next operation */ dw->need_ranging_clock = - (info->flags & MCPS802154_TX_FRAME_KEEP_RANGING_CLOCK) != 0; + (config->flags & + MCPS802154_TX_FRAME_CONFIG_KEEP_RANGING_CLOCK) != 0; fail: if (rc) /* Ensure SPI queuing mode disabled on error */ @@ -3596,6 +3731,8 @@ static int dw3000_rx_frame(struct dw3000 *dw, rc = -ENOMEM; goto err_spi; } + if (dw->pctt.enabled) + len += IEEE802154_FCS_LEN; buffer = skb_put(skb, len); /* Directly read data from the IC to the buffer */ rc = dw3000_rx_read_data(dw, buffer, len, 0); @@ -3614,7 +3751,7 @@ static int dw3000_rx_frame(struct dw3000 *dw, spin_unlock_irqrestore(&rx->lock, flags); /* Print the received frame in hexadecimal characters */ if (unlikely(DEBUG)) { - dev_dbg(dw->dev, "frame info: len=%lu, rxflags=0x%.2x", len, + dev_dbg(dw->dev, "frame config: len=%lu, rxflags=0x%.2x", len, data->rx_flags); if (skb) print_hex_dump_bytes("dw3000: frame data: ", @@ -4682,7 +4819,7 @@ static int dw3000_wakeup_done_to_tx(struct dw3000 *dw) trace_dw3000_wakeup_done_to_tx(dw); dss->next_operational_state = dw->current_operational_state; - return dw3000_do_tx_frame(dw, &dss->tx_info, dss->tx_skb, + return dw3000_do_tx_frame(dw, &dss->tx_config, dss->tx_skb, dss->frame_idx); } @@ -4698,7 +4835,7 @@ static int dw3000_wakeup_done_to_rx(struct dw3000 *dw) trace_dw3000_wakeup_done_to_rx(dw); dss->next_operational_state = dw->current_operational_state; - return dw3000_do_rx_enable(dw, &dss->rx_info, dss->frame_idx); + return dw3000_do_rx_enable(dw, &dss->rx_config, dss->frame_idx); } /** @@ -4715,15 +4852,26 @@ static int dw3000_wakeup_done_to_idle(struct dw3000 *dw) trace_dw3000_wakeup_done_to_idle(dw); if (dw->idle_timeout) { u32 now_dtu = dw3000_get_dtu_time(dw); - int idle_duration_us = - DTU_TO_US(dw->idle_timeout_dtu - now_dtu); + int idle_duration_dtu = dw->idle_timeout_dtu - now_dtu; /* TODO: * 2 timers implemented (idle, deep_sleep), * or a better FSM (enter, leave, events, state) - * should help to follow/repect timestamps expected. */ - dw3000_wakeup_timer_start(dw, idle_duration_us); - return 0; + * should help to follow/respect timestamp expected. */ + /* WORKAROUND: On a delayed wake-up (by CPU high load), + * the idle_timeout_dtu date can be in the past. And to avoid + * to program a timer in the past, process the idle_timeout_cb + * immediately when duration is negative or equal to 0. + * For NFCC_COEX, the NFC software will enter in "LATE" + * processing, and return immediately too. Thus the CPU high + * load will not broken dw3000 driver and mac layer. */ + if (idle_duration_dtu > 0) { + int idle_duration_us = DTU_TO_US(idle_duration_dtu); + + dw3000_wakeup_timer_start(dw, idle_duration_us); + return 0; + } + trace_dw3000_wakeup_done_to_idle_late(dw); } return dw3000_enqueue_generic(dw, &cmd); } @@ -4765,16 +4913,19 @@ int dw3000_idle(struct dw3000 *dw, bool timestamp, u32 timestamp_dtu, dw->idle_timeout_dtu = timestamp_dtu; if (timestamp) { int deepsleep_delay_us; + int idle_delay_dtu; int idle_delay_us; bool is_sleeping = dw->current_operational_state == DW3000_OP_STATE_DEEP_SLEEP; cur_time_dtu = dw3000_get_dtu_time(dw); - idle_delay_us = DTU_TO_US(timestamp_dtu - cur_time_dtu); - if (idle_delay_us < 0) { + idle_delay_dtu = + timestamp_dtu - cur_time_dtu - dw->llhw->anticip_dtu; + if (idle_delay_dtu < 0) { rc = -ETIME; goto eof; } + idle_delay_us = DTU_TO_US(idle_delay_dtu); /* Warning: timestamp_dtu have already taken in consideration * WakeUp latency! */ deepsleep_delay_us = idle_delay_us; @@ -4786,6 +4937,7 @@ int dw3000_idle(struct dw3000 *dw, bool timestamp, u32 timestamp_dtu, * which haven't the same timeout_dtu! */ if (deepsleep_delay_us < 0) { dw3000_deepsleep_wakeup_now(dw, idle_timeout_cb, + dw->idle_timeout_dtu, DW3000_OP_STATE_MAX); rc = 0; goto eof; @@ -5144,7 +5296,7 @@ int dw3000_set_shortaddr(struct dw3000 *dw, __le16 val) } /** - * dw3000_configure_hw_addr_filt() - Set device's hardware address filtering + * dw3000_configure_hw_addr_filt() - Set device's hardware address filtering * parameters * @dw: the DW device * @changed: bitfields of flags indicating parameters which have changed @@ -5574,6 +5726,23 @@ int dw3000_disable_autoack(struct dw3000 *dw, bool force) } /** + * dw3000_enable_auto_fcs() - Enable or disable the automatic FCS adding + * @dw: the DW device + * @on: enable the auto FCS adding if true, otherwise disable it + * + * Return: zero on success, else a negative error code. + */ +int dw3000_enable_auto_fcs(struct dw3000 *dw, bool on) +{ + if (!on) { + return dw3000_reg_or32(dw, DW3000_SYS_CFG_ID, 0, + DW3000_SYS_CFG_DIS_FCS_TX_BIT_MASK); + } + return dw3000_reg_and32(dw, DW3000_SYS_CFG_ID, 0, + (~DW3000_SYS_CFG_DIS_FCS_TX_BIT_MASK)); +} + +/** * dw3000_otp_read32() - 32 bits OTP memory read. * @dw: the DW device * @addr: address in the OTP memory @@ -6589,22 +6758,17 @@ int dw3000_read_frame_cir_data(struct dw3000 *dw, /* Get packet type */ cir->type = stsMode; - /* Get tdoa */ - if (info->flags & MCPS802154_RX_FRAME_INFO_RANGING_PDOA) { - cir->tdoa = info->ranging_pdoa_rad_q11; - } else { - cir->tdoa = 0; - } - /* CIA algorithm have to finish before results reading */ if (!(dw->rx.flags & DW3000_RX_FLAG_CIA)) { rc = -ENODATA; goto read_frame_cir_error; } + dw->full_cia_read = false; rc = dw3000_read_ciaregs(dw); if (rc) goto read_frame_cir_error; + dw->full_cia_read = true; dw3000_read_fp_power(dw, stsMode); dw3000_read_cir_acc(dw, stsMode); rc = dw3000_acc_clken(dw, true); @@ -7106,6 +7270,10 @@ void dw3000_init_config(struct dw3000 *dw) /* Set default antenna ports configuration */ dw->calib_data.ant[0].port = 0; dw->calib_data.ant[1].port = 1; + /* By default WiFi Coex is enabled */ + dw->coex_enabled = true; + for (i = 0; i < DW3000_CALIBRATION_CHANNEL_MAX; i++) + dw->calib_data.ch[i].wifi_coex_enabled = true; /* Ensure power stats timing start at load time */ dw->power.cur_state = DW3000_PWR_OFF; dw->power.stats[DW3000_PWR_OFF].count = 1; @@ -7218,8 +7386,8 @@ static inline int dw3000_isr_handle_spi_ready(struct dw3000 *dw, dw3000_dtu_to_ktime(dw, dw->sleep_enter_dtu); next_date_dtu = dss->next_operational_state == DW3000_OP_STATE_RX ? - dss->rx_info.timestamp_dtu : - dss->tx_info.timestamp_dtu; + dss->rx_config.timestamp_dtu : + dss->tx_config.timestamp_dtu; trace_dw3000_wakeup_done(dw, sleep_ns / 1000, dw->sleep_enter_dtu, next_date_dtu, dss->next_operational_state); @@ -7298,7 +7466,7 @@ setuperror: if (wakeup_done_cb == dw3000_wakeup_done_to_tx) mcps802154_tx_too_late(dw->llhw); else - mcps802154_rx_too_late(dw->llhw); + mcps802154_rx_too_late(dw->llhw); rc = 0; } @@ -7392,10 +7560,6 @@ static inline int dw3000_isr_handle_rx_call_handler(struct dw3000 *dw, u32 eof_dtu; int rc; - /* Report statistics */ - rc = dw3000_rx_stats_inc(dw, DW3000_STATS_RX_GOOD); - if (unlikely(rc)) - return rc; /* Store LDE/STS RX errors in rx_flags */ if (isr->status & DW3000_SYS_STATUS_CIAERR_BIT_MASK) isr->rx_flags |= DW3000_RX_FLAG_CER; @@ -7411,7 +7575,7 @@ static inline int dw3000_isr_handle_rx_call_handler(struct dw3000 *dw, if (unlikely(rc)) return rc; isr->rx_flags |= DW3000_RX_FLAG_TS; /* don't read it again later */ - eof_dtu = (u32)(isr->ts_rctu / DW3000_RCTU_PER_DTU) + + eof_dtu = dw3000_sys_time_rctu_to_dtu(dw, isr->ts_rctu) + dw3000_frame_duration_dtu(dw, isr->datalength, false); /* Update power statistics */ dw3000_power_stats(dw, DW3000_PWR_IDLE, eof_dtu); @@ -7482,7 +7646,7 @@ static inline int dw3000_isr_handle_rxto_event(struct dw3000 *dw, u32 status) DW3000_CHIP_PER_DTU; dw3000_power_stats(dw, DW3000_PWR_IDLE, end_dtu); /* Report statistics */ - rc = dw3000_rx_stats_inc(dw, DW3000_STATS_RX_TO); + rc = dw3000_rx_stats_inc(dw, DW3000_STATS_RX_TO, NULL); if (unlikely(rc)) goto err; /* Inform upper layer */ @@ -7500,7 +7664,6 @@ static inline int dw3000_isr_handle_rxerr_event(struct dw3000 *dw, u32 status) { struct mcps802154_llhw *llhw = dw->llhw; enum mcps802154_rx_error_type error; - int rc; /* If rx_disable() callback was called, we can't call mcps802154_rx_error() */ if (dw3000_rx_busy(dw, true)) @@ -7508,7 +7671,11 @@ static inline int dw3000_isr_handle_rxerr_event(struct dw3000 *dw, u32 status) /* Update power statistics */ dw3000_power_stats(dw, DW3000_PWR_IDLE, 0); /* Map error to mcps802154_rx_error_type enum */ - if (status & DW3000_SYS_STATUS_RXSTO_BIT_MASK) { + if (status & (DW3000_SYS_STATUS_RXPTO_BIT_MASK | + DW3000_SYS_STATUS_RXFTO_BIT_MASK)) { + dev_dbg(dw->dev, "rx preamble timeout\n"); + error = MCPS802154_RX_ERROR_TIMEOUT; + } else if (status & DW3000_SYS_STATUS_RXSTO_BIT_MASK) { dev_dbg(dw->dev, "rx sfd timeout\n"); error = MCPS802154_RX_ERROR_SFD_TIMEOUT; } else if (status & DW3000_SYS_STATUS_ARFE_BIT_MASK) { @@ -7522,7 +7689,7 @@ static inline int dw3000_isr_handle_rxerr_event(struct dw3000 *dw, u32 status) error = MCPS802154_RX_ERROR_BAD_CKSUM; } else if (status & DW3000_SYS_STATUS_RXPHE_BIT_MASK) { dev_dbg(dw->dev, "rx phr error\n"); - error = MCPS802154_RX_ERROR_OTHER; + error = MCPS802154_RX_ERROR_PHR_DECODE; } else if (status & DW3000_SYS_STATUS_RXFSL_BIT_MASK) { dev_dbg(dw->dev, "rx sync loss\n"); error = MCPS802154_RX_ERROR_OTHER; @@ -7530,15 +7697,11 @@ static inline int dw3000_isr_handle_rxerr_event(struct dw3000 *dw, u32 status) dev_dbg(dw->dev, "rx error 0x%x\n", status); error = MCPS802154_RX_ERROR_OTHER; } - /* Report statistics */ - rc = dw3000_rx_stats_inc(dw, DW3000_STATS_RX_ERROR); - if (unlikely(rc)) - goto err; /* Report RX error event */ mcps802154_rx_error(llhw, error); -err: + WARN_ON_ONCE(dw3000_rx_busy(dw, false)); - return rc; + return 0; } static inline int dw3000_isr_handle_tx_event(struct dw3000 *dw, @@ -7931,7 +8094,8 @@ static ssize_t dw3000_sysfs_show(struct kobject *kobj, "Run state:\n\tcount:\t%llu\n\tdur ns:\t%llu\n" "Idle state:\n\tcount:\t%llu\n\tdur ns:\t%llu\n" "Tx state:\n\tcount:\t%llu\n\tdur ns:\t%llu\n" - "Rx state:\n\tcount:\t%llu\n\tdur ns:\t%llu\n", + "Rx state:\n\tcount:\t%llu\n\tdur ns:\t%llu\n" + "Interrupts:\n\tcount:\t%lld\n", dw->power.stats[DW3000_PWR_OFF].count, dw->power.stats[DW3000_PWR_OFF].dur, dw->power.stats[DW3000_PWR_DEEPSLEEP].count, @@ -7940,7 +8104,8 @@ static ssize_t dw3000_sysfs_show(struct kobject *kobj, dw->power.stats[DW3000_PWR_RUN].dur, dw->power.stats[DW3000_PWR_IDLE].count, idle_dur, dw->power.stats[DW3000_PWR_TX].count, tx_ns, - dw->power.stats[DW3000_PWR_RX].count, rx_ns); + dw->power.stats[DW3000_PWR_RX].count, rx_ns, + (s64)atomic64_read(&dw->power.interrupts)); return ret; } @@ -7957,6 +8122,7 @@ static ssize_t dw3000_sysfs_store(struct kobject *kobj, dw->power.stats[cstate].count = 1; if (dw->power.cur_state > DW3000_PWR_RUN) dw->power.stats[dw->power.cur_state].count = 1; + atomic64_set(&dw->power.interrupts, 0); } return length; } @@ -7985,3 +8151,189 @@ void dw3000_sysfs_remove(struct dw3000 *dw) kobject_del(&dw->sysfs_power_dir); } } + +/** + * dw3000_nfcc_coex_prepare_config() - Prepare the configuration before nfcc + * coex. + * @dw: The DW device. + * + * Return: Zero on success, else a negative error code. + */ +int dw3000_nfcc_coex_prepare_config(struct dw3000 *dw) +{ + int rc; + + trace_dw3000_nfcc_coex_prepare_config(dw); + + /* Disable any rx or tx command in progress. */ + rc = dw3000_rx_disable(dw); + if (rc) + return rc; + + /* May need to resync to avoid drift. */ + rc = dw3000_may_resync(dw); + if (rc) + return rc; + + /* Reset Wait-for-Response Time. */ + rc = dw3000_setrxaftertxdelay(dw, 0); + if (rc) + return rc; + + /* Reset the reception timeout. */ + rc = dw3000_setrxtimeout(dw, 0); + if (rc) + return rc; + + /* + * Disable RXPTO behavior for two reasons: + * - CCC firmware doesn't manage it. As the RXPTO_EN is false, + * then ccc is blocked. + * - Update cached value in dw3000 context for next use. + */ + return dw3000_setpreambledetecttimeout(dw, 0); +} + +/** + * dw3000_nfcc_coex_restore_config() - Restore the configuration after nfcc + * coex. + * @dw: The DW device. + * + * Some cache is reset to force the reconfiguration. + * Some RF parameters are reconfigured. + * + * Return: Zero on success, else a negative error code. + */ +int dw3000_nfcc_coex_restore_config(struct dw3000 *dw) +{ + struct dw3000_local_data *local = &dw->data; + struct dw3000_config *config = &dw->config; + int rc; + + trace_dw3000_nfcc_coex_restore_config(dw); + + /* + * We want to restore the configuration after nfcc slot. + */ + + /* + * Clear security registers related cache. + * The STS parameters will be reset during "set_sts_params" call. + */ + memset(local->sts_key, 0, AES_KEYSIZE_128); + memset(local->sts_iv, 0, AES_BLOCK_SIZE); + dw->config.stsLength = 0; + + /* Clear all cache variables to force the reconfiguration. */ + local->rx_timeout_pac = 0; + local->w4r_time = 0; + local->ack_time = 0; + local->tx_fctrl = 0; + local->rx_frame_timeout_dly = 0; + local->ack_time = 0; + + /* Configure the SYS_CFG register. */ + rc = dw3000_configure_sys_cfg(dw, config); + if (rc) + return rc; + + /* WiFi coexistence initialisation. */ + rc = dw3000_coex_init(dw); + if (rc) + return rc; + + /* Configure antenna selection GPIO if any. */ + rc = dw3000_config_antenna_gpios(dw); + if (rc) + return rc; + + /* + * Reset cached antenna config to ensure GPIO are reconfigured + * correctly. + */ + dw->config.ant[0] = -1; + dw->config.ant[1] = -1; + + /* Select the events that will trigger an interrupt. */ + rc = dw3000_set_interrupt(dw, DW3000_SYS_STATUS_TRX, + DW3000_ENABLE_INT_ONLY); + if (rc) + return rc; + + /* + * PLL already is locked but some RF parameters could be changed. + * So we reprogram the Xtal, the DGC, the ADC, ... + */ + + /* Xtal trim could be changed. */ + rc = dw3000_prog_xtrim(dw); + if (rc) + return rc; + + /* Configure PLL coarse code, if needed. */ + if (dw->chip_ops->prog_pll_coarse_code) { + rc = dw->chip_ops->prog_pll_coarse_code(dw); + if (rc) { + dev_err(dw->dev, "device coarse code setup has failed (%d)\n", rc); + return rc; + } + } + + /* Configure delays. */ + rc = dw3000_set_antenna_delay(dw, 0); + if (rc) + return rc; + + rc = dw3000_reconfigure_hw_addr_filt(dw); + if (rc) + return rc; + + /* Do some device specific initialisation if any. */ + rc = dw->chip_ops->init(dw); + if (rc) { + dev_err(dw->dev, "device chip specific init has failed (%d)\n", + rc); + return rc; + } + + /* Reconfigure all dependent channels. */ + rc = dw3000_configure_chan(dw); + if (rc) + return rc; + + rc = dw3000_pgf_cal(dw, 1); + if (rc) + return rc; + + /* Calibrate ADC offset, if needed, after DGC configuration and after PLL lock. */ + if (dw->chip_ops->adc_offset_calibration) { + rc = dw->chip_ops->adc_offset_calibration(dw); + if (rc) + return rc; + } + + /* Setup TX preamble size, data rate and SDF timeout count. */ + rc = dw3000_configure_preamble_length_and_datarate(dw, false); + if (rc) + return rc; + + /* PHR rate. */ + rc = dw3000_configure_phr_rate(dw); + if (rc) + return rc; + + /* + * Ensure STS fields are double-buffered if enabled, also enable stats + * if configured in module parameters. + */ + rc = dw3000_configure_ciadiag(dw, dw->stats.enabled, + dw->data.dblbuffon ? + DW3000_CIA_DIAG_LOG_DBL_MID : + DW3000_CIA_DIAG_LOG_DBL_OFF); + if (rc) { + dev_err(dw->dev, "device CIA DIAG setup has failed (%d)\n", rc); + return rc; + } + + return 0; +} diff --git a/kernel/drivers/net/ieee802154/dw3000_core.h b/kernel/drivers/net/ieee802154/dw3000_core.h index 6294d40..e3c28a8 100644 --- a/kernel/drivers/net/ieee802154/dw3000_core.h +++ b/kernel/drivers/net/ieee802154/dw3000_core.h @@ -374,6 +374,7 @@ int dw3000_configure_sts_iv(struct dw3000 *dw, const u8 *iv); int dw3000_load_sts_iv(struct dw3000 *dw); int dw3000_configure_sys_cfg(struct dw3000 *dw, struct dw3000_config *config); int dw3000_configure_hw_addr_filt(struct dw3000 *dw, unsigned long changed); +int dw3000_enable_auto_fcs(struct dw3000 *dw, bool on); int dw3000_clear_sys_status(struct dw3000 *dw, u32 clear_bits); int dw3000_clear_dss_status(struct dw3000 *dw, u8 clear_bits); @@ -383,12 +384,20 @@ int dw3000_read_rdb_status(struct dw3000 *dw, u8 *status); int dw3000_read_sys_status(struct dw3000 *dw, u32 *status); int dw3000_read_sys_time(struct dw3000 *dw, u32 *sys_time); +int dw3000_rx_store_rssi(struct dw3000 *dw, struct dw3000_rssi *rssi, + u8 pkt_sts); +int dw3000_rx_calc_rssi(struct dw3000 *dw, struct dw3000_rssi *rssi, + struct mcps802154_rx_frame_info *info, u8 pkt_sts); +int dw3000_rx_stats_inc(struct dw3000 *dw, const enum dw3000_stats_items item, + struct dw3000_rssi *rssi); + u32 dw3000_get_dtu_time(struct dw3000 *dw); int dw3000_forcetrxoff(struct dw3000 *dw); int dw3000_do_rx_enable(struct dw3000 *dw, - const struct mcps802154_rx_info *info, int frame_idx); + const struct mcps802154_rx_frame_config *config, + int frame_idx); int dw3000_rx_enable(struct dw3000 *dw, bool rx_delayed, u32 date_dtu, u32 timeout_pac); int dw3000_rx_disable(struct dw3000 *dw); @@ -400,9 +409,9 @@ void dw3000_rx_stats_clear(struct dw3000 *dw); int dw3000_enable_autoack(struct dw3000 *dw, bool force); int dw3000_disable_autoack(struct dw3000 *dw, bool force); -struct mcps802154_tx_frame_info; +struct mcps802154_tx_frame_config; int dw3000_do_tx_frame(struct dw3000 *dw, - const struct mcps802154_tx_frame_info *info, + const struct mcps802154_tx_frame_config *config, struct sk_buff *skb, int frame_idx); int dw3000_tx_setcwtone(struct dw3000 *dw, bool on); @@ -446,6 +455,7 @@ int dw3000_idle(struct dw3000 *dw, bool timestamp, u32 timestamp_dtu, enum operational_state next_operational_state); int dw3000_deepsleep_wakeup_now(struct dw3000 *dw, dw3000_idle_timeout_cb idle_timeout_cb, + u32 timestamp_dtu, enum operational_state next_operational_state); int dw3000_can_deep_sleep(struct dw3000 *dw, int delay_us); int dw3000_trace_rssi_info(struct dw3000 *dw, u32 regid, char *chipver); @@ -453,6 +463,8 @@ int dw3000_trace_rssi_info(struct dw3000 *dw, u32 regid, char *chipver); int dw3000_testmode_continuous_tx_start(struct dw3000 *dw, u32 frame_length, u32 rate); int dw3000_testmode_continuous_tx_stop(struct dw3000 *dw); +int dw3000_nfcc_coex_prepare_config(struct dw3000 *dw); +int dw3000_nfcc_coex_restore_config(struct dw3000 *dw); /* Preamble length related information. */ struct dw3000_plen_info { @@ -498,6 +510,21 @@ static inline int dw3000_compute_shr_dtu(struct dw3000 *dw) return shr_symb * chip_per_symb / DW3000_CHIP_PER_DTU; } +static inline int compute_shr_dtu_from_conf( + const struct mcps802154_hrp_uwb_params *hrp_uwb_params) +{ + const int preamble_symb = hrp_uwb_params->psr; + const int chip_per_symb = + _prf_info[hrp_uwb_params->prf == MCPS802154_PRF_64 ? + DW3000_PRF_64M : + DW3000_PRF_16M] + .chip_per_symb; + /* The only possible sfd number of symbols is 8. */ + const int sfd_symb = 8; + const int shr_symb = preamble_symb + sfd_symb; + return shr_symb * chip_per_symb / DW3000_CHIP_PER_DTU; +} + static inline int dw3000_compute_symbol_dtu(struct dw3000 *dw) { const int chip_per_symb = @@ -654,6 +681,23 @@ static inline u32 dw3000_sys_time_to_dtu(struct dw3000 *dw, u32 sys_time, } /** + * dw3000_sys_time_rctu_to_dtu() - compute current DTU time from RCTU. + * @dw: the DW device. + * @timestamp_rctu: The DW device RX_STAMP register value in RCTU to convert to DTU. + * The RCTU, Ranging Counter Time Unit, is approximately 15.65 picoseconds long. + * + * Return: The corresponding DTU time. + */ +static inline u32 dw3000_sys_time_rctu_to_dtu(struct dw3000 *dw, + u64 timestamp_rctu) +{ + u32 sys_time = (u32)(timestamp_rctu / DW3000_RCTU_PER_SYS); + u32 dtu_near = dw3000_get_dtu_time(dw) - DW3000_DTU_FREQ; + + return dw3000_sys_time_to_dtu(dw, sys_time, dtu_near); +} + +/** * dw3000_reset_rctu_conv_state() - reset RCTU converter * @dw: the DW device * @@ -676,6 +720,13 @@ static inline void dw3000_resync_rctu_conv_state(struct dw3000 *dw) dw->rctu_conv.state = ALIGNED; } +static inline int pac_to_dly(struct mcps802154_llhw *llhw, int pac) +{ + struct dw3000 *dw = llhw->priv; + + return (pac * dw->chips_per_pac / DW3000_CHIP_PER_DLY); +} + static inline int dtu_to_pac(struct mcps802154_llhw *llhw, int timeout_dtu) { struct dw3000 *dw = llhw->priv; diff --git a/kernel/drivers/net/ieee802154/dw3000_core_reg.h b/kernel/drivers/net/ieee802154/dw3000_core_reg.h index 7ba4a8b..7b77584 100644 --- a/kernel/drivers/net/ieee802154/dw3000_core_reg.h +++ b/kernel/drivers/net/ieee802154/dw3000_core_reg.h @@ -137,6 +137,9 @@ #define DW3000_TX_FCTRL_TXFLEN_BIT_LEN (10U) #define DW3000_TX_FCTRL_TXFLEN_BIT_MASK 0x3ffU +/* register RX_FWTO */ +#define DW3000_RX_FWTO_ID 0x34 + /* register SYS_ENABLE_LO */ #define DW3000_SYS_ENABLE_LO_ID 0x3c #define DW3000_SYS_ENABLE_LO_LEN (4U) diff --git a/kernel/drivers/net/ieee802154/dw3000_core_tests.c b/kernel/drivers/net/ieee802154/dw3000_core_tests.c index 15f3f85..ed69b63 100644 --- a/kernel/drivers/net/ieee802154/dw3000_core_tests.c +++ b/kernel/drivers/net/ieee802154/dw3000_core_tests.c @@ -5,6 +5,7 @@ static u64 kunit_get_boottime_ns(void); /* Replace ktime_get_boottime_ns calls to kunit_get_boottime_ns calls. */ #define ktime_get_boottime_ns kunit_get_boottime_ns +#define dw3000_get_dtu_time kunit_dw3000_get_dtu_time #define trace_dw3000_power_stats(dw, state, boot_time_ns, len_or_date) \ do { \ } while (0) @@ -118,6 +119,7 @@ const struct dw3000_prf_info _prf_info[] = { /* Static variable declarations */ static u64 kunit_boot_time_ns; +static u32 kunit_dtu_time; /** * kunit_get_boottime_ns() - ktime_get_boottime_ns replacement for tests @@ -130,6 +132,17 @@ static u64 kunit_get_boottime_ns(void) return kunit_boot_time_ns; } +/** + * kunit_dw3000_get_dtu_time() - dw3000_get_dtu_time kunit wrapper + * @dw: the DW device + * + * Return: The current simulated DTU time. + */ +u32 kunit_dw3000_get_dtu_time(struct dw3000 *dw) +{ + return kunit_dtu_time; +} + /* Define the test cases. */ static void dw3000_ktime_to_dtu_test_basic(struct kunit *test) @@ -213,6 +226,52 @@ static void dw3000_sys_time_to_dtu_test_basic(struct kunit *test) dw3000_sys_time_to_dtu(dw, 0x13ea64u, dtu_near)); } +static void dw3000_sys_time_rctu_to_dtu_test_basic(struct kunit *test) +{ + struct dw3000 *dw = kunit_kzalloc(test, sizeof(*dw), GFP_KERNEL); + + /* Ensure allocation succeeded. */ + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dw); + + /* Tests with dtu_sync == 0, sys_time_sync == 0 and dtu_near == 0. + * Set kunit_dtu_time to DW3000_DTU_FREQ to get dtu_near == 0 in + * dw3000_sys_time_rctu_to_dtu(). */ + kunit_dtu_time = DW3000_DTU_FREQ; + KUNIT_EXPECT_EQ(test, 0u, dw3000_sys_time_rctu_to_dtu(dw, 0)); + KUNIT_EXPECT_EQ(test, 1u, + dw3000_sys_time_rctu_to_dtu(dw, DW3000_RCTU_PER_DTU)); + + /* Tests with dtu_near == 10000. + * Set kunit_dtu_time to DW3000_DTU_FREQ + 10000 to get dtu_near == 10000 + * in dw3000_sys_time_rctu_to_dtu(). */ + kunit_dtu_time = DW3000_DTU_FREQ + 10000; + KUNIT_EXPECT_EQ( + test, 1005u, + dw3000_sys_time_rctu_to_dtu(dw, 1005 * DW3000_RCTU_PER_DTU)); + + /* Tests with dtu_sync == 1000000/16 & sys_time_sync == 1000000 */ + dw->sys_time_sync = 1000000; + dw->dtu_sync = 1000000 >> 4; + KUNIT_EXPECT_EQ( + test, 10000u, + dw3000_sys_time_rctu_to_dtu(dw, 10000 * DW3000_RCTU_PER_DTU)); + + /* Tests with real values from traces: + * timestamp_rctu: 63852263355 + * dtu_sync: 13025 + * sys_time_sync: 5414 + * dtu_near: 4349 + * dw3000_sys_time_rctu_to_dtu() return: 15601618 + * + * Set kunit_dtu_time to DW3000_DTU_FREQ + 4349 to get dtu_near == 4349 + * in dw3000_sys_time_rctu_to_dtu(). */ + kunit_dtu_time = DW3000_DTU_FREQ + 4349; + dw->dtu_sync = 13025; + dw->sys_time_sync = 5414; + KUNIT_EXPECT_EQ(test, 15601618u, + dw3000_sys_time_rctu_to_dtu(dw, 63852263355)); +} + static void power_stats_test_setup(struct dw3000 *dw) { struct dw3000_power *pwr = &dw->power; @@ -441,6 +500,7 @@ static struct kunit_case dw3000_core_test_cases[] = { KUNIT_CASE(dw3000_dtu_to_ktime_test_basic), KUNIT_CASE(dw3000_dtu_to_sys_time_test_basic), KUNIT_CASE(dw3000_sys_time_to_dtu_test_basic), + KUNIT_CASE(dw3000_sys_time_rctu_to_dtu_test_basic), KUNIT_CASE(dw3000_power_stats_test_basic), KUNIT_CASE(dw3000_power_stats_test_tx), KUNIT_CASE(dw3000_power_stats_test_rx), diff --git a/kernel/drivers/net/ieee802154/dw3000_mcps.c b/kernel/drivers/net/ieee802154/dw3000_mcps.c index 4fc6875..8c7c5c2 100644 --- a/kernel/drivers/net/ieee802154/dw3000_mcps.c +++ b/kernel/drivers/net/ieee802154/dw3000_mcps.c @@ -37,6 +37,7 @@ #include "dw3000_pctt_mcps.h" #include "dw3000_coex.h" #include "dw3000_cir.h" +#include "dw3000_power_stats.h" static int completion_active(struct completion *completion) { @@ -47,12 +48,14 @@ static int completion_active(struct completion *completion) #endif } -static inline u32 timestamp_rctu_to_dtu(struct dw3000 *dw, u64 timestamp_rctu); static inline u64 timestamp_rctu_to_rmarker_rctu(struct dw3000 *dw, u64 timestamp_rctu, u32 rmarker_dtu); -static inline u32 tx_rmarker_offset(struct dw3000 *dw, int ant_set_id) +static inline u32 +tx_rmarker_offset(struct dw3000 *dw, + const struct mcps802154_channel *channel_params, + int ant_set_id) { struct dw3000_config *config = &dw->config; const struct dw3000_antenna_calib *ant_calib; @@ -60,6 +63,9 @@ static inline u32 tx_rmarker_offset(struct dw3000 *dw, int ant_set_id) int chanidx; int prfidx; s8 ant_idx1, ant_idx2; + int chan = channel_params ? channel_params->channel : config->chan; + int pcode = channel_params ? channel_params->preamble_code : + config->txCode; if (ant_set_id < 0 || ant_set_id >= ANTSET_ID_MAX) { dev_err(dw->dev, @@ -86,10 +92,10 @@ static inline u32 tx_rmarker_offset(struct dw3000 *dw, int ant_set_id) ant_calib = &dw->calib_data.ant[ant_idx1]; - chanidx = config->chan == 9 ? DW3000_CALIBRATION_CHANNEL_9 : - DW3000_CALIBRATION_CHANNEL_5; - prfidx = config->txCode >= 9 ? DW3000_CALIBRATION_PRF_64MHZ : - DW3000_CALIBRATION_PRF_16MHZ; + chanidx = chan == 9 ? DW3000_CALIBRATION_CHANNEL_9 : + DW3000_CALIBRATION_CHANNEL_5; + prfidx = pcode >= 9 ? DW3000_CALIBRATION_PRF_64MHZ : + DW3000_CALIBRATION_PRF_16MHZ; ant_calib_prf = &ant_calib->ch[chanidx].prf[prfidx]; @@ -240,7 +246,7 @@ static void stop(struct mcps802154_llhw *llhw) struct do_tx_frame_params { struct sk_buff *skb; - const struct mcps802154_tx_frame_info *info; + const struct mcps802154_tx_frame_config *config; int frame_idx; }; @@ -249,30 +255,30 @@ static int do_tx_frame(struct dw3000 *dw, const void *in, void *out) const struct do_tx_frame_params *params = (const struct do_tx_frame_params *)in; - return dw3000_do_tx_frame(dw, params->info, params->skb, + return dw3000_do_tx_frame(dw, params->config, params->skb, params->frame_idx); } static int tx_frame(struct mcps802154_llhw *llhw, struct sk_buff *skb, - const struct mcps802154_tx_frame_info *info, int frame_idx, - int next_delay_dtu) + const struct mcps802154_tx_frame_config *config, + int frame_idx, int next_delay_dtu) { struct dw3000 *dw = llhw->priv; struct do_tx_frame_params params = { .skb = skb, - .info = info, + .config = config, .frame_idx = frame_idx }; struct dw3000_stm_command cmd = { do_tx_frame, ¶ms, NULL }; /* Check data : no data if SP3, must have data otherwise */ - if (((info->flags & MCPS802154_TX_FRAME_STS_MODE_MASK) == - MCPS802154_TX_FRAME_SP3) != !skb) + if (((config->flags & MCPS802154_TX_FRAME_CONFIG_STS_MODE_MASK) == + MCPS802154_TX_FRAME_CONFIG_SP3) != !skb) return -EINVAL; return dw3000_enqueue_generic(dw, &cmd); } struct do_rx_frame_params { - const struct mcps802154_rx_info *info; + const struct mcps802154_rx_frame_config *config; int frame_idx; }; @@ -281,15 +287,15 @@ static int do_rx_enable(struct dw3000 *dw, const void *in, void *out) const struct do_rx_frame_params *params = (const struct do_rx_frame_params *)in; - return dw3000_do_rx_enable(dw, params->info, params->frame_idx); + return dw3000_do_rx_enable(dw, params->config, params->frame_idx); } static int rx_enable(struct mcps802154_llhw *llhw, - const struct mcps802154_rx_info *info, int frame_idx, - int next_delay_dtu) + const struct mcps802154_rx_frame_config *config, + int frame_idx, int next_delay_dtu) { struct dw3000 *dw = llhw->priv; - struct do_rx_frame_params params = { .info = info, + struct do_rx_frame_params params = { .config = config, .frame_idx = frame_idx }; struct dw3000_stm_command cmd = { do_rx_enable, ¶ms, NULL }; @@ -387,6 +393,30 @@ static int get_ranging_sts_fom(struct mcps802154_llhw *llhw, /* DW3000 only support one STS segment. */ info->ranging_sts_fom[0] = clamp(1 + sts_acc_qual * 254 / sts_acc_max, 1, 255); + /* Set FoM of all other segments to maximum value so that they do not + * cause quality check failure. */ + memset(&info->ranging_sts_fom[1], 0xFF, MCPS802154_STS_N_SEGS_MAX - 1); + return ret; +} + +static int rx_get_rssi(struct dw3000 *dw, struct mcps802154_rx_frame_info *info, + const enum dw3000_stats_items item) +{ + struct dw3000_config *config = &dw->config; + int ret = 0; + + if (dw->stats.enabled || info->flags & MCPS802154_RX_FRAME_INFO_RSSI) { + struct dw3000_rssi rssi; + u8 sts = config->stsMode & DW3000_STS_BASIC_MODES_MASK; + ret = dw3000_rx_store_rssi(dw, &rssi, sts); + if (ret) { + info->flags &= ~MCPS802154_RX_FRAME_INFO_RSSI; + return ret; + } + if (dw->stats.enabled) + dw3000_rx_stats_inc(dw, item, &rssi); + ret = dw3000_rx_calc_rssi(dw, &rssi, info, sts); + } return ret; } @@ -437,7 +467,7 @@ static int rx_get_frame(struct mcps802154_llhw *llhw, struct sk_buff **skb, MCPS802154_RX_FRAME_INFO_RANGING_STS_TIMESTAMP_RCTU); else { u32 rmarker_dtu = - timestamp_rctu_to_dtu(dw, timestamp_rctu); + dw3000_sys_time_rctu_to_dtu(dw, timestamp_rctu); u64 rmarker_rctu = timestamp_rctu_to_rmarker_rctu( dw, timestamp_rctu, rmarker_dtu); info->timestamp_rctu = @@ -470,12 +500,6 @@ static int rx_get_frame(struct mcps802154_llhw *llhw, struct sk_buff **skb, if (unlikely(ret)) goto error; } - /* In case of PDoA. */ - if (info->flags & MCPS802154_RX_FRAME_INFO_RANGING_PDOA) { - info->ranging_pdoa_rad_q11 = dw3000_read_pdoa(dw); - info->ranging_aoa_rad_q11 = - dw3000_pdoa_to_aoa_lut(dw, info->ranging_pdoa_rad_q11); - } /* In case of STS */ if (info->flags & MCPS802154_RX_FRAME_INFO_RANGING_STS_TIMESTAMP_RCTU) { u64 sts_ts_rctu; @@ -519,6 +543,17 @@ static int rx_get_frame(struct mcps802154_llhw *llhw, struct sk_buff **skb, info->ranging_tracking_interval_rctu = 1 << 26; } + /* If dbgfs file is opened & waiting for data, fill structure and wake-up reading process */ + if (dw->cir_data && completion_active(&dw->cir_data->complete)) { + ret = dw3000_read_frame_cir_data(dw, info, pkt_ts); + if (ret) + goto error; + } + /* Report statistics and if required process RSSI */ + ret = rx_get_rssi(dw, info, DW3000_STATS_RX_GOOD); + if (ret) + goto error; + /* Keep only implemented. */ info->flags &= (MCPS802154_RX_FRAME_INFO_TIMESTAMP_RCTU | MCPS802154_RX_FRAME_INFO_TIMESTAMP_DTU | @@ -527,16 +562,9 @@ static int rx_get_frame(struct mcps802154_llhw *llhw, struct sk_buff **skb, MCPS802154_RX_FRAME_INFO_RANGING_PDOA_FOM | MCPS802154_RX_FRAME_INFO_RANGING_STS_FOM | MCPS802154_RX_FRAME_INFO_RANGING_STS_TIMESTAMP_RCTU | - MCPS802154_RX_FRAME_INFO_RANGING_OFFSET); + MCPS802154_RX_FRAME_INFO_RANGING_OFFSET | + MCPS802154_RX_FRAME_INFO_RSSI); trace_dw3000_return_int_u32(dw, info->flags, *skb ? (*skb)->len : 0); - - /* If dbgfs file is opened & waiting for data, fill structure and wake-up reading process */ - if (dw->cir_data && completion_active(&dw->cir_data->complete)) { - ret = dw3000_read_frame_cir_data(dw, info, pkt_ts); - if (ret) - goto error; - } - return 0; error: @@ -559,15 +587,65 @@ static int rx_get_error_frame(struct mcps802154_llhw *llhw, if (info->flags & MCPS802154_RX_FRAME_INFO_TIMESTAMP_RCTU) { if (dw3000_read_rx_timestamp(dw, &info->timestamp_rctu)) info->flags &= ~MCPS802154_RX_FRAME_INFO_TIMESTAMP_RCTU; - } else { - /* Not implemented */ - info->flags = 0; } + /* Report statistics and if required process RSSI */ + ret = rx_get_rssi(dw, info, DW3000_STATS_RX_ERROR); + if (ret) + goto error; + /* Keep only implemented. */ + info->flags &= (MCPS802154_RX_FRAME_INFO_TIMESTAMP_RCTU | + MCPS802154_RX_FRAME_INFO_RSSI); + error: trace_dw3000_return_int_u32(dw, ret, info->flags); return ret; } +/** + * rx_get_measurement - Update measurement. + * @llhw: Low-level device pointer. + * @rx_ctx: Custom rx context (can be NULL). + * @info: Measurement to update. + * + * Return: 0 or error. + */ +static int rx_get_measurement(struct mcps802154_llhw *llhw, void *rx_ctx, + struct mcps802154_rx_measurement_info *info) +{ + struct dw3000 *dw = llhw->priv; + + if (info->flags & MCPS802154_RX_MEASUREMENTS_AOAS) { + info->aoas[0].pdoa_rad_q11 = dw3000_read_pdoa(dw); + info->aoas[0].aoa_rad_q11 = + dw3000_pdoa_to_aoa_lut(dw, info->aoas[0].pdoa_rad_q11); + info->n_aoas = 1; + } + + /* TODO: UWB-4961 Usage of a mcps802154_rx_frame_info is a + * workaround used until rx_get_rssi() can be fully removed + * from rx_get_frame(). */ + if (info->flags & MCPS802154_RX_MEASUREMENTS_RSSIS) { + struct mcps802154_rx_frame_info frame_info; + int ret; + + frame_info.flags = MCPS802154_RX_FRAME_INFO_RSSI; + ret = rx_get_rssi(dw, &frame_info, DW3000_STATS_RX_GOOD); + if (ret) { + info->n_rssis = 0; + } else { + info->rssis_q1[0] = frame_info.rssi; + /* Only one RSSI computed per frame */ + info->n_rssis = 1; + } + } + + /* Keep only implemented. */ + info->flags &= MCPS802154_RX_MEASUREMENTS_AOAS | + MCPS802154_RX_MEASUREMENTS_RSSIS; + + return 0; +} + static int dw3000_handle_idle_timeout(struct dw3000 *dw) { /* MCPS feeback must be done outside driver kthread. */ @@ -660,14 +738,6 @@ static int get_current_timestamp_dtu(struct mcps802154_llhw *llhw, return ret; } -static inline u32 timestamp_rctu_to_dtu(struct dw3000 *dw, u64 timestamp_rctu) -{ - u32 sys_time = (u32)(timestamp_rctu / DW3000_RCTU_PER_SYS); - u32 dtu_near = dw3000_get_dtu_time(dw) - DW3000_DTU_FREQ; - - return dw3000_sys_time_to_dtu(dw, sys_time, dtu_near); -} - static inline u64 timestamp_rctu_to_rmarker_rctu(struct dw3000 *dw, u64 timestamp_rctu, u32 rmarker_dtu) @@ -696,15 +766,20 @@ static inline u64 timestamp_rctu_to_rmarker_rctu(struct dw3000 *dw, return rmarker_rctu; } -static u64 tx_timestamp_dtu_to_rmarker_rctu(struct mcps802154_llhw *llhw, - u32 tx_timestamp_dtu, - int ant_set_id) +static u64 tx_timestamp_dtu_to_rmarker_rctu( + struct mcps802154_llhw *llhw, u32 tx_timestamp_dtu, + const struct mcps802154_hrp_uwb_params *hrp_uwb_params, + const struct mcps802154_channel *channel_params, int ant_set_id) { struct dw3000 *dw = llhw->priv; struct dw3000_rctu_conv *rctu = &dw->rctu_conv; - const u32 rmarker_dtu = tx_timestamp_dtu + llhw->shr_dtu; - const u32 ant_offset = tx_rmarker_offset(dw, ant_set_id); + const u32 shr_dtu = hrp_uwb_params ? + compute_shr_dtu_from_conf(hrp_uwb_params) : + llhw->shr_dtu; + const u32 rmarker_dtu = tx_timestamp_dtu + shr_dtu; + const u32 ant_offset = + tx_rmarker_offset(dw, channel_params, ant_set_id); u64 rmarker_rctu; s64 rmarker_diff_dtu; @@ -822,9 +897,11 @@ static int do_set_hrp_uwb_params(struct dw3000 *dw, const void *in, void *out) dss->config_changed |= changed; return 0; } - if (changed & DW3000_PREAMBLE_LENGTH_CHANGED) { - /* Reconfigure preamble size and SDF timeout count */ - rc = dw3000_configure_preamble_length_and_datarate(dw, false); + if (changed & + (DW3000_PREAMBLE_LENGTH_CHANGED | DW3000_DATA_RATE_CHANGED)) { + /* reconfigure data rate and preamble size if needed. */ + rc = dw3000_configure_preamble_length_and_datarate( + dw, !(changed & DW3000_PREAMBLE_LENGTH_CHANGED)); if (rc) return rc; } @@ -839,16 +916,69 @@ static int do_set_hrp_uwb_params(struct dw3000 *dw, const void *in, void *out) if (rc) return rc; } - /* If DW3000_PREAMBLE_LENGTH_CHANGED is set, data rate is already configured, skip it. */ - if ((changed & DW3000_DATA_RATE_CHANGED) && - !(changed & DW3000_PREAMBLE_LENGTH_CHANGED)) - rc = dw3000_configure_preamble_length_and_datarate(dw, true); + + if (changed & (DW3000_SFD_CHANGED | DW3000_PREAMBLE_LENGTH_CHANGED)) + dw3000_update_timings(dw); return rc; } -int set_hrp_uwb_params(struct mcps802154_llhw *llhw, int prf, int psr, - int sfd_selector, int phr_rate, int data_rate) +static int check_hrp_uwb_params(struct mcps802154_llhw *llhw, + const struct mcps802154_hrp_uwb_params *params) +{ + switch (params->prf) { + case MCPS802154_PRF_16: + case MCPS802154_PRF_64: + break; + case MCPS802154_PRF_125: + case MCPS802154_PRF_250: + return -ENOTSUPP; + default: + return -EINVAL; + } + switch (params->psr) { + case MCPS802154_PSR_32: + case MCPS802154_PSR_64: + case MCPS802154_PSR_128: + case MCPS802154_PSR_256: + case MCPS802154_PSR_1024: + case MCPS802154_PSR_4096: + break; + case MCPS802154_PSR_16: + case MCPS802154_PSR_24: + case MCPS802154_PSR_48: + case MCPS802154_PSR_96: + return -ENOTSUPP; + default: + return -EINVAL; + } + switch (params->sfd_selector) { + case MCPS802154_SFD_4A: + case MCPS802154_SFD_4Z_8: + break; + case MCPS802154_SFD_4Z_4: + case MCPS802154_SFD_4Z_16: + case MCPS802154_SFD_4Z_32: + return -ENOTSUPP; + default: + return -EINVAL; + } + switch (params->data_rate) { + case MCPS802154_DATA_RATE_6M81: + case MCPS802154_DATA_RATE_850K: + break; + case MCPS802154_DATA_RATE_7M80: + case MCPS802154_DATA_RATE_27M2: + case MCPS802154_DATA_RATE_31M2: + return -ENOTSUPP; + default: + return -EINVAL; + } + return 0; +} + +int set_hrp_uwb_params(struct mcps802154_llhw *llhw, + const struct mcps802154_hrp_uwb_params *params) { unsigned long changed = 0; struct dw3000 *dw = llhw->priv; @@ -856,6 +986,7 @@ int set_hrp_uwb_params(struct mcps802154_llhw *llhw, int prf, int psr, struct dw3000_stm_command cmd = { do_set_hrp_uwb_params, &changed, NULL }; int ret; + int psr, sfd_selector, phr_hi_rate, data_rate; /* The prf parameter is not used. This is due to the specificity of * the DW3000 chip where the prf is not programmed explicitly, @@ -864,44 +995,58 @@ int set_hrp_uwb_params(struct mcps802154_llhw *llhw, int prf, int psr, */ /* Check parameters early */ - switch (psr) { - case DW3000_PLEN_64: - case DW3000_PLEN_32: - case DW3000_PLEN_72: - case DW3000_PLEN_128: - case DW3000_PLEN_256: - case DW3000_PLEN_512: - case DW3000_PLEN_1024: - case DW3000_PLEN_1536: - case DW3000_PLEN_2048: - case DW3000_PLEN_4096: + ret = check_hrp_uwb_params(llhw, params); + if (ret) + return ret; + + switch (params->psr) { + case MCPS802154_PSR_32: + psr = DW3000_PLEN_32; + break; + case MCPS802154_PSR_128: + psr = DW3000_PLEN_128; + break; + case MCPS802154_PSR_256: + psr = DW3000_PLEN_256; + break; + case MCPS802154_PSR_1024: + psr = DW3000_PLEN_1024; + break; + case MCPS802154_PSR_4096: + psr = DW3000_PLEN_4096; break; default: - return -EINVAL; + psr = DW3000_PLEN_64; + break; } - switch (sfd_selector) { - case DW3000_SFD_TYPE_STD: - case DW3000_SFD_TYPE_DW_8: - case DW3000_SFD_TYPE_DW_16: - case DW3000_SFD_TYPE_4Z: + switch (params->sfd_selector) { + case MCPS802154_SFD_4A: + sfd_selector = DW3000_SFD_TYPE_STD; break; default: - return -EINVAL; + sfd_selector = DW3000_SFD_TYPE_4Z; + break; } - if (phr_rate != DW3000_PHRRATE_STD && phr_rate != DW3000_PHRRATE_DTA) - return -EINVAL; + switch (params->data_rate) { + case MCPS802154_DATA_RATE_850K: + data_rate = DW3000_BR_850K; + break; + default: + data_rate = DW3000_BR_6M8; + break; + } - if (data_rate != DW3000_BR_850K && data_rate != DW3000_BR_6M8) - return -EINVAL; + phr_hi_rate = params->phr_hi_rate ? DW3000_PHRRATE_DTA : + DW3000_PHRRATE_STD; /* Detect configuration change(s) */ if (config->txPreambLength != psr) changed |= DW3000_PREAMBLE_LENGTH_CHANGED; if (config->sfdType != sfd_selector) changed |= DW3000_SFD_CHANGED; - if (config->phrRate != phr_rate) + if (config->phrRate != phr_hi_rate) changed |= DW3000_PHR_RATE_CHANGED; if (config->dataRate != data_rate) changed |= DW3000_DATA_RATE_CHANGED; @@ -911,7 +1056,7 @@ int set_hrp_uwb_params(struct mcps802154_llhw *llhw, int prf, int psr, /* Update configuration structure */ config->txPreambLength = psr; config->sfdType = sfd_selector; - config->phrRate = phr_rate; + config->phrRate = phr_hi_rate; config->dataRate = data_rate; /* Reconfigure the chip with it if needed */ @@ -1138,13 +1283,13 @@ static int do_vendor_cmd(struct dw3000 *dw, const void *in, void *out) const struct do_vendor_params *params = in; switch (params->subcmd) { - case DW3000_VENDOR_CMD_PCTT_SETUP_HW: + case LLHW_VENDOR_CMD_PCTT_SETUP_HW: return dw3000_pctt_vendor_cmd(dw, params->vendor_id, params->subcmd, params->data, params->data_len); - case DW3000_VENDOR_CMD_NFCC_COEX_HANDLE_ACCESS: - case DW3000_VENDOR_CMD_NFCC_COEX_GET_ACCESS_INFORMATION: - case DW3000_VENDOR_CMD_NFCC_COEX_STOP: + case LLHW_VENDOR_CMD_NFCC_COEX_HANDLE_ACCESS: + case LLHW_VENDOR_CMD_NFCC_COEX_GET_ACCESS_INFORMATION: + case LLHW_VENDOR_CMD_NFCC_COEX_STOP: return dw3000_nfcc_coex_vendor_cmd(dw, params->vendor_id, params->subcmd, params->data, params->data_len); @@ -1176,6 +1321,66 @@ static int vendor_cmd(struct mcps802154_llhw *llhw, u32 vendor_id, u32 subcmd, return dw3000_enqueue_generic(dw, &cmd); } +static int get_antenna_caps(struct mcps802154_llhw *llhw, int ant_idx, + uint32_t *caps) +{ + struct dw3000 *dw = llhw->priv; + const struct dw3000_antenna_calib *ant_calib; + + if (ant_idx < 0 || ant_idx >= ANTMAX) { + dev_err(dw->dev, "Bad antenna number (%d)\n", ant_idx); + return -EINVAL; + } + + ant_calib = &dw->calib_data.ant[ant_idx]; + *caps = ant_calib->caps; + return 0; +} + +/** + * get_power_stats() - Forward vendor commands processing to dw3000 function. + * @llhw: Low-level hardware without MCPS. + * @pwr_stats: mcps802154_power_stats structure to be filled. + * + * Return: 0 on success or negative error code. + */ +static int get_power_stats(struct mcps802154_llhw *llhw, + struct mcps802154_power_stats *pwr_stats) +{ + struct dw3000 *dw = llhw->priv; + u64 idle_dur, rx_ns, tx_ns; + + /* Update the power statistics if needed. */ + if (dw->power.cur_state <= DW3000_PWR_IDLE) + dw3000_power_stats(dw, dw->power.cur_state, 0); + /* TX/RX are kept in DTU unit. Convert it here to limit conversion error */ + rx_ns = dw->power.stats[DW3000_PWR_RX].dur * 10000 / + (DW3000_DTU_FREQ / 100000); + tx_ns = dw->power.stats[DW3000_PWR_TX].dur * 10000 / + (DW3000_DTU_FREQ / 100000); + idle_dur = dw->power.stats[DW3000_PWR_RUN].dur - tx_ns - rx_ns; + + pwr_stats->power_state_stats[MCPS802154_PWR_STATE_OFF].dur = + dw->power.stats[DW3000_PWR_OFF].dur; + pwr_stats->power_state_stats[MCPS802154_PWR_STATE_OFF].count = + dw->power.stats[DW3000_PWR_OFF].count; + pwr_stats->power_state_stats[MCPS802154_PWR_STATE_SLEEP].dur = + dw->power.stats[DW3000_PWR_DEEPSLEEP].dur; + pwr_stats->power_state_stats[MCPS802154_PWR_STATE_SLEEP].count = + dw->power.stats[DW3000_PWR_DEEPSLEEP].count; + pwr_stats->power_state_stats[MCPS802154_PWR_STATE_IDLE].dur = idle_dur; + pwr_stats->power_state_stats[MCPS802154_PWR_STATE_IDLE].count = + dw->power.stats[DW3000_PWR_IDLE].count; + pwr_stats->power_state_stats[MCPS802154_PWR_STATE_RX].dur = rx_ns; + pwr_stats->power_state_stats[MCPS802154_PWR_STATE_RX].count = + dw->power.stats[DW3000_PWR_RX].count; + pwr_stats->power_state_stats[MCPS802154_PWR_STATE_TX].dur = tx_ns; + pwr_stats->power_state_stats[MCPS802154_PWR_STATE_TX].count = + dw->power.stats[DW3000_PWR_TX].count; + pwr_stats->interrupts = atomic64_read(&dw->power.interrupts); + return 0; +} + static const struct mcps802154_ops dw3000_mcps_ops = { .start = start, .stop = stop, @@ -1184,6 +1389,7 @@ static const struct mcps802154_ops dw3000_mcps_ops = { .rx_disable = rx_disable, .rx_get_frame = rx_get_frame, .rx_get_error_frame = rx_get_error_frame, + .rx_get_measurement = rx_get_measurement, .idle = idle, .reset = reset, .get_current_timestamp_dtu = get_current_timestamp_dtu, @@ -1192,6 +1398,7 @@ static const struct mcps802154_ops dw3000_mcps_ops = { .compute_frame_duration_dtu = compute_frame_duration_dtu, .set_channel = set_channel, .set_hrp_uwb_params = set_hrp_uwb_params, + .check_hrp_uwb_params = check_hrp_uwb_params, .set_sts_params = set_sts_params, .set_hw_addr_filt = set_hw_addr_filt, .set_txpower = set_txpower, @@ -1202,6 +1409,8 @@ static const struct mcps802154_ops dw3000_mcps_ops = { .get_calibration = get_calibration, .list_calibration = list_calibration, .vendor_cmd = vendor_cmd, + .get_antenna_caps = get_antenna_caps, + .get_power_stats = get_power_stats, MCPS802154_TESTMODE_CMD(dw3000_tm_cmd) }; @@ -1229,7 +1438,18 @@ struct dw3000 *dw3000_mcps_alloc(struct device *dev) llhw->hw->flags = (IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT | IEEE802154_HW_PROMISCUOUS | IEEE802154_HW_RX_OMIT_CKSUM); - llhw->flags = llhw->hw->flags; + llhw->flags = + (MCPS802154_LLHW_BPRF | MCPS802154_LLHW_DATA_RATE_850K | + MCPS802154_LLHW_DATA_RATE_6M81 | + MCPS802154_LLHW_PHR_DATA_RATE_850K | + MCPS802154_LLHW_PHR_DATA_RATE_6M81 | MCPS802154_LLHW_PRF_16 | + MCPS802154_LLHW_PRF_64 | MCPS802154_LLHW_PSR_32 | + MCPS802154_LLHW_PSR_64 | MCPS802154_LLHW_PSR_128 | + MCPS802154_LLHW_PSR_256 | MCPS802154_LLHW_PSR_1024 | + MCPS802154_LLHW_PSR_4096 | MCPS802154_LLHW_SFD_4A | + MCPS802154_LLHW_SFD_4Z_8 | MCPS802154_LLHW_STS_SEGMENT_1 | + MCPS802154_LLHW_AOA_AZIMUTH | MCPS802154_LLHW_AOA_ELEVATION | + MCPS802154_LLHW_AOA_FOM); llhw->hw->phy->supported.channels[4] = DW3000_SUPPORTED_CHANNELS; @@ -1254,6 +1474,8 @@ struct dw3000 *dw3000_mcps_alloc(struct device *dev) llhw->hw->phy->current_channel = dw->config.chan; llhw->hw->phy->current_page = 4; llhw->current_preamble_code = dw->config.txCode; + /* AoA/PDoA filtering. */ + llhw->rx_ctx_size = sizeof(struct dw3000_rx_ctx); return dw; } diff --git a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex.h b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex.h index 4e04239..0bffafc 100644 --- a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex.h +++ b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex.h @@ -27,7 +27,6 @@ #include <net/vendor_cmd.h> /* Main defines */ -#define DW3000_NFCC_COEX_VER_ID 2 #define DW3000_NFCC_COEX_SIGNATURE_STR "QORVO" #define DW3000_NFCC_COEX_SIGNATURE_LEN 5 #define DW3000_NFCC_COEX_MAX_NB_TLV 12 @@ -51,6 +50,19 @@ #define DW3000_NFCC_COEX_DTU_PER_UUS_POWER 4 /* To use with left shift. */ /** + * enum dw3000_nfcc_coex_send - Type of message to send. + * + * @DW3000_NFCC_COEX_SEND_CLK_SYNC: Clock sync message. + * @DW3000_NFCC_COEX_SEND_CLK_OFFSET: Clock offset message. + * @DW3000_NFCC_COEX_SEND_STOP: Stop message. + */ +enum dw3000_nfcc_coex_send { + DW3000_NFCC_COEX_SEND_CLK_SYNC, + DW3000_NFCC_COEX_SEND_CLK_OFFSET, + DW3000_NFCC_COEX_SEND_STOP, +}; + +/** * struct dw3000_nfcc_coex_msg - Message read/write from/to scratch memory. */ struct dw3000_nfcc_coex_msg { @@ -122,7 +134,7 @@ struct dw3000_nfcc_coex { /** * @access_info: Access information to provide to upper layer. */ - struct dw3000_vendor_cmd_nfcc_coex_get_access_info access_info; + struct llhw_vendor_cmd_nfcc_coex_get_access_info access_info; /** * @session_time0_dtu: Timestamp used as reference between NFCC and AP. */ @@ -156,9 +168,9 @@ struct dw3000_nfcc_coex { */ bool configured; /** - * @sync_time_needed: True when clock_sync frame must be send. + * @send: Type of message to send. */ - bool sync_time_needed; + enum dw3000_nfcc_coex_send send; /** * @first_rx_message: False after the first valid msg received. */ @@ -167,6 +179,10 @@ struct dw3000_nfcc_coex { * @watchdog_timer: Watchdog timer to detect spi not bring back. */ struct timer_list watchdog_timer; + /** + * @version: Protocol version to use. + */ + u8 version; }; #endif /* __DW3000_NFCC_COEX_H */ diff --git a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_core.c b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_core.c index 702c8a3..9d8aef9 100644 --- a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_core.c +++ b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_core.c @@ -30,7 +30,10 @@ #include <linux/module.h> -unsigned dw3000_nfcc_coex_margin_dtu = DW3000_ANTICIP_DTU; +/* dw3000_nfcc_coex_margin_dtu: + * - Can't be bigger than ANTICIP_DTU (trouble with CLOCK_SYNC). + * - Lower than 4ms is really dangerous. */ +unsigned dw3000_nfcc_coex_margin_dtu = US_TO_DTU(16000); module_param_named(nfcc_coex_margin_dtu, dw3000_nfcc_coex_margin_dtu, uint, 0444); MODULE_PARM_DESC( @@ -117,7 +120,7 @@ static int dw3000_nfcc_coex_disable_SPIxMAVAIL_interrupts(struct dw3000 *dw) static void dw3000_nfcc_coex_update_access_info( struct dw3000 *dw, const struct dw3000_nfcc_coex_buffer *buffer) { - struct dw3000_vendor_cmd_nfcc_coex_get_access_info *access_info = + struct llhw_vendor_cmd_nfcc_coex_get_access_info *access_info = &dw->nfcc_coex.access_info; struct dw3000_nfcc_coex_rx_msg_info rx_msg_info = {}; int r; @@ -132,9 +135,12 @@ static void dw3000_nfcc_coex_update_access_info( access_info->stop = !rx_msg_info.next_slot_found; access_info->watchdog_timeout = false; if (rx_msg_info.next_slot_found) { + /* Request the handle earlier to the mac layer. */ access_info->next_timestamp_dtu = - rx_msg_info.next_timestamp_dtu; - access_info->next_duration_dtu = rx_msg_info.next_duration_dtu; + rx_msg_info.next_timestamp_dtu - + dw3000_nfcc_coex_margin_dtu; + access_info->next_duration_dtu = rx_msg_info.next_duration_dtu + + dw3000_nfcc_coex_margin_dtu; } return; @@ -168,11 +174,13 @@ int dw3000_nfcc_coex_configure(struct dw3000 *dw) return r; } } - r = dw3000_rx_disable(dw); + r = dw3000_nfcc_coex_prepare_config(dw); if (r) { - trace_dw3000_nfcc_coex_warn(dw, "rx disable failed"); + trace_dw3000_nfcc_coex_warn(dw, + "prepare the configuration fails"); return r; } + r = dw3000_nfcc_coex_enable_SPIxMAVAIL_interrupts(dw); if (r) { trace_dw3000_nfcc_coex_err( @@ -334,12 +342,10 @@ void dw3000_nfcc_coex_init(struct dw3000 *dw) * dw3000_nfcc_coex_enable() - Enable NFCC coexistence. * @dw: Driver context. * @channel: Channel number (5 or 9). - * @sync_time_needed: True when it's the first access. * * Return: 0 on success, else an error. */ -int dw3000_nfcc_coex_enable(struct dw3000 *dw, u8 channel, - bool sync_time_needed) +int dw3000_nfcc_coex_enable(struct dw3000 *dw, u8 channel) { struct dw3000_nfcc_coex *nfcc_coex = &dw->nfcc_coex; @@ -349,7 +355,6 @@ int dw3000_nfcc_coex_enable(struct dw3000 *dw, u8 channel, /* Save current channel. */ nfcc_coex->original_channel = dw->config.chan; - nfcc_coex->sync_time_needed = sync_time_needed; nfcc_coex->configured = false; nfcc_coex->enabled = true; /* Set the new channel. */ @@ -385,7 +390,9 @@ int dw3000_nfcc_coex_disable(struct dw3000 *dw) if (r) return r; } - dw->nfcc_coex.configured = false; + r = dw3000_nfcc_coex_restore_config(dw); + if (r) + return r; } return 0; } diff --git a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_core.h b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_core.h index 15fb442..26bfa64 100644 --- a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_core.h +++ b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_core.h @@ -34,8 +34,7 @@ int dw3000_nfcc_coex_cancel_watchdog(struct dw3000 *dw); int dw3000_nfcc_coex_spi1_avail(struct dw3000 *dw); int dw3000_nfcc_coex_idle_timeout(struct dw3000 *dw); void dw3000_nfcc_coex_init(struct dw3000 *dw); -int dw3000_nfcc_coex_enable(struct dw3000 *dw, u8 channel, - bool sync_time_needed); +int dw3000_nfcc_coex_enable(struct dw3000 *dw, u8 channel); int dw3000_nfcc_coex_disable(struct dw3000 *dw); int dw3000_nfcc_coex_configure(struct dw3000 *dw); diff --git a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_mcps.c b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_mcps.c index 6e759ed..2214859 100644 --- a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_mcps.c +++ b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_mcps.c @@ -30,10 +30,48 @@ #define DW3000_NFCC_COEX_WATCHDOG_DEFAULT_DURATION_MS 24000 +static int dw3000_nfcc_coex_wakeup_and_send(struct dw3000 *dw, + s32 idle_duration_dtu, + u32 send_timestamp_dtu) +{ + struct dw3000_nfcc_coex *nfcc_coex = &dw->nfcc_coex; + int r; + + trace_dw3000_nfcc_coex_wakeup_and_send( + dw, nfcc_coex->send, idle_duration_dtu, send_timestamp_dtu); + + if (idle_duration_dtu > dw->llhw->anticip_dtu) { + r = dw3000_idle(dw, true, send_timestamp_dtu, + dw3000_nfcc_coex_idle_timeout, + DW3000_OP_STATE_MAX); + goto wakeup_and_send_end; + } else if (dw->current_operational_state == + DW3000_OP_STATE_DEEP_SLEEP) { + r = dw3000_deepsleep_wakeup_now(dw, + dw3000_nfcc_coex_idle_timeout, + send_timestamp_dtu, + DW3000_OP_STATE_MAX); + goto wakeup_and_send_end; + } + + r = dw3000_nfcc_coex_configure(dw); + if (r) + goto wakeup_and_send_end; + r = dw3000_nfcc_coex_message_send(dw); + if (r) + goto wakeup_and_send_end; + return 0; + +wakeup_and_send_end: + if (r) + dw3000_nfcc_coex_disable(dw); + return r; +} + /** * dw3000_nfcc_coex_handle_access() - handle access to provide to NFCC. * @dw: Driver context. - * @data: Adress of handle access information. + * @data: Address of handle access information. * @data_len: Number of byte of the data object. * * Return: 0 on success, else an error. @@ -41,10 +79,10 @@ static int dw3000_nfcc_coex_handle_access(struct dw3000 *dw, void *data, int data_len) { - const struct dw3000_vendor_cmd_nfcc_coex_handle_access *info = data; + const struct llhw_vendor_cmd_nfcc_coex_handle_access *info = data; struct dw3000_nfcc_coex *nfcc_coex = &dw->nfcc_coex; const u32 dtu_per_ms = dw->llhw->dtu_freq_hz / 1000; - u32 now_dtu, message_send_timestamp_dtu; + u32 now_dtu; s32 idle_duration_dtu; int r; @@ -56,19 +94,32 @@ static int dw3000_nfcc_coex_handle_access(struct dw3000 *dw, void *data, return -EBUSY; } - r = dw3000_nfcc_coex_enable(dw, info->chan, info->start); + r = dw3000_nfcc_coex_enable(dw, info->chan); if (r) return r; now_dtu = dw3000_get_dtu_time(dw); - message_send_timestamp_dtu = - info->timestamp_dtu - dw3000_nfcc_coex_margin_dtu; - idle_duration_dtu = message_send_timestamp_dtu - now_dtu; + idle_duration_dtu = info->timestamp_dtu - now_dtu; trace_dw3000_nfcc_coex_handle_access(dw, info, idle_duration_dtu); - /* Save start session date, to retrieve MSB bits lost for next date. */ - nfcc_coex->access_start_dtu = info->timestamp_dtu; - /* Build the when spi must be released. */ + nfcc_coex->send = info->start ? DW3000_NFCC_COEX_SEND_CLK_SYNC : + DW3000_NFCC_COEX_SEND_CLK_OFFSET; + if (info->start) { + nfcc_coex->version = info->version; + /* + * Save first start session date, to retrieve MSB bits lost + * for next received timestamp through session_time0_dtu. + * It's saved because between session_time0_dtu and + * access_start_dtu, a deep sleep can occur, and so + * `dtu_to_sys_time` must be call after the wake up. + * Between access_start_dtu and now, the duration can be less + * than 2 ms (fira slot) in multi-region feature. + * So the margin is like a second anticip dtu to add to provide + * time for NFC handling. + */ + nfcc_coex->access_start_dtu = + info->timestamp_dtu + dw3000_nfcc_coex_margin_dtu; + } nfcc_coex->watchdog_timer.expires = jiffies + msecs_to_jiffies((info->timestamp_dtu - now_dtu) / dtu_per_ms + @@ -76,38 +127,14 @@ static int dw3000_nfcc_coex_handle_access(struct dw3000 *dw, void *data, add_timer(&nfcc_coex->watchdog_timer); /* Send message and so release the SPI close to the nfc_coex_margin. */ - message_send_timestamp_dtu = - info->timestamp_dtu - dw3000_nfcc_coex_margin_dtu; - if (idle_duration_dtu > 0) { - r = dw3000_idle(dw, true, message_send_timestamp_dtu, - dw3000_nfcc_coex_idle_timeout, - DW3000_OP_STATE_MAX); - goto handle_access_end; - } else if (dw->current_operational_state == - DW3000_OP_STATE_DEEP_SLEEP) { - r = dw3000_deepsleep_wakeup_now( - dw, dw3000_nfcc_coex_idle_timeout, DW3000_OP_STATE_MAX); - goto handle_access_end; - } - - r = dw3000_nfcc_coex_configure(dw); - if (r) - goto handle_access_end; - r = dw3000_nfcc_coex_message_send(dw); - if (r) - goto handle_access_end; - return 0; - -handle_access_end: - if (r) - dw3000_nfcc_coex_disable(dw); - return r; + return dw3000_nfcc_coex_wakeup_and_send(dw, idle_duration_dtu, + info->timestamp_dtu); } /** * dw3000_nfcc_coex_get_access_information() - Forward access info cached. * @dw: Driver context. - * @data: Adress where to write access information. + * @data: Address where to write access information. * @data_len: Number of byte of the data object. * * Return: 0 on success, else an error. @@ -115,7 +142,7 @@ handle_access_end: static int dw3000_nfcc_coex_get_access_information(struct dw3000 *dw, void *data, int data_len) { - const struct dw3000_vendor_cmd_nfcc_coex_get_access_info *access_info = + const struct llhw_vendor_cmd_nfcc_coex_get_access_info *access_info = &dw->nfcc_coex.access_info; if (!data || data_len != sizeof(*access_info)) @@ -128,22 +155,60 @@ static int dw3000_nfcc_coex_get_access_information(struct dw3000 *dw, /** * dw3000_nfcc_coex_stop() - Stop NFCC. * @dw: Driver context. + * @data: Address of stop information. + * @data_len: Number of byte of the data object. * * Return: 0 on success, else an error. */ -static int dw3000_nfcc_coex_stop(struct dw3000 *dw) +static int dw3000_nfcc_coex_stop(struct dw3000 *dw, void *data, int data_len) { + const struct llhw_vendor_cmd_nfcc_coex_stop *info = data; + struct dw3000_nfcc_coex *nfcc_coex = &dw->nfcc_coex; + const u32 dtu_per_ms = dw->llhw->dtu_freq_hz / 1000; + u32 now_dtu, send_timestamp_dtu; + s32 idle_duration_dtu; int r; - /* Cancel the idle timeout, and ignore the deepsleep state. */ - r = dw3000_idle_cancel_timer(dw); - if (r) - return r; - /* Cancel the watchdog which have a bigger timeout. */ - r = dw3000_nfcc_coex_cancel_watchdog(dw); + if (data_len && data_len != sizeof(*info)) + return -EINVAL; + + if (timer_pending(&nfcc_coex->watchdog_timer)) { + trace_dw3000_nfcc_coex_err(dw, "watchdog timer is pending"); + return -EBUSY; + } + + r = dw3000_nfcc_coex_enable(dw, dw->config.chan); if (r) return r; - return dw3000_nfcc_coex_disable(dw); + + nfcc_coex->send = DW3000_NFCC_COEX_SEND_STOP; + + if (info) { + now_dtu = dw3000_get_dtu_time(dw); + send_timestamp_dtu = info->timestamp_dtu; + idle_duration_dtu = send_timestamp_dtu - now_dtu; + nfcc_coex->watchdog_timer.expires = + jiffies + + msecs_to_jiffies( + (info->timestamp_dtu - now_dtu) / dtu_per_ms + + DW3000_NFCC_COEX_WATCHDOG_DEFAULT_DURATION_MS); + nfcc_coex->version = info->version; + } else { + send_timestamp_dtu = 0; + idle_duration_dtu = 0; + nfcc_coex->watchdog_timer.expires = + jiffies + + msecs_to_jiffies( + DW3000_NFCC_COEX_WATCHDOG_DEFAULT_DURATION_MS); + /* Cancel wakeup timer launch by idle() */ + dw3000_idle_cancel_timer(dw); + } + + add_timer(&nfcc_coex->watchdog_timer); + + /* Send message and so release the SPI close to the nfc_coex_margin. */ + return dw3000_nfcc_coex_wakeup_and_send(dw, idle_duration_dtu, + send_timestamp_dtu); } /** @@ -165,13 +230,13 @@ int dw3000_nfcc_coex_vendor_cmd(struct dw3000 *dw, u32 vendor_id, u32 subcmd, return -EOPNOTSUPP; switch (subcmd) { - case DW3000_VENDOR_CMD_NFCC_COEX_HANDLE_ACCESS: + case LLHW_VENDOR_CMD_NFCC_COEX_HANDLE_ACCESS: return dw3000_nfcc_coex_handle_access(dw, data, data_len); - case DW3000_VENDOR_CMD_NFCC_COEX_GET_ACCESS_INFORMATION: + case LLHW_VENDOR_CMD_NFCC_COEX_GET_ACCESS_INFORMATION: return dw3000_nfcc_coex_get_access_information(dw, data, data_len); - case DW3000_VENDOR_CMD_NFCC_COEX_STOP: - return dw3000_nfcc_coex_stop(dw); + case LLHW_VENDOR_CMD_NFCC_COEX_STOP: + return dw3000_nfcc_coex_stop(dw, data, data_len); default: return -EINVAL; } diff --git a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_msg.c b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_msg.c index c0f2172..db6f253 100644 --- a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_msg.c +++ b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_msg.c @@ -64,11 +64,11 @@ void dw3000_nfcc_coex_header_put(struct dw3000 *dw, { struct dw3000_nfcc_coex_msg *msg = &buffer->msg; - trace_dw3000_nfcc_coex_header_put(dw, DW3000_NFCC_COEX_VER_ID, + trace_dw3000_nfcc_coex_header_put(dw, dw->nfcc_coex.version, dw->nfcc_coex.tx_seq_num); memcpy(msg->signature, DW3000_NFCC_COEX_SIGNATURE_STR, DW3000_NFCC_COEX_SIGNATURE_LEN); - msg->ver_id = DW3000_NFCC_COEX_VER_ID; + msg->ver_id = dw->nfcc_coex.version; msg->seqnum = dw->nfcc_coex.tx_seq_num; msg->nb_tlv = 0; buffer->tlvs_len = 0; @@ -139,6 +139,24 @@ static void dw3000_nfcc_coex_clock_offset_payload_put( } /** + * dw3000_nfcc_coex_stop_session_payload_put() - Fill stop session payload. + * @dw: Driver context. + * @buffer: Buffer to set with help of handle_access. + * @session_id: Session id to stop. + */ +static void dw3000_nfcc_coex_stop_session_payload_put( + struct dw3000 *dw, struct dw3000_nfcc_coex_buffer *buffer, + u32 session_id) +{ + trace_dw3000_nfcc_coex_stop_session_payload_put(dw, session_id); + + dw3000_nfcc_coex_header_put(dw, buffer); + + dw3000_nfcc_coex_tlv_u32_put( + buffer, DW3000_NFCC_COEX_TLV_TYPE_STOP_SESSION, session_id); +} + +/** * dw3000_nfcc_coex_message_send() - Write message for NFCC and release SPI1. * @dw: Driver context. * @@ -150,17 +168,25 @@ int dw3000_nfcc_coex_message_send(struct dw3000 *dw) /* Build the absolute sys time offset. */ u32 offset_sys_time = (dw->dtu_sync << DW3000_DTU_PER_SYS_POWER) - dw->sys_time_sync; + u32 clock_offset_sys_time; - if (dw->nfcc_coex.sync_time_needed) { - dw->nfcc_coex.sync_time_needed = false; + switch (dw->nfcc_coex.send) { + case DW3000_NFCC_COEX_SEND_CLK_SYNC: dw3000_nfcc_coex_clock_sync_payload_put(dw, &buffer); - } else { + break; + default: + case DW3000_NFCC_COEX_SEND_CLK_OFFSET: /* Compute the clock correction to forward to NFCC. */ - u32 clock_offset_sys_time = + clock_offset_sys_time = offset_sys_time - dw->nfcc_coex.prev_offset_sys_time; /* Build the message with the clock update to forward. */ dw3000_nfcc_coex_clock_offset_payload_put( dw, &buffer, -clock_offset_sys_time); + break; + case DW3000_NFCC_COEX_SEND_STOP: + dw3000_nfcc_coex_stop_session_payload_put( + dw, &buffer, DW3000_NFCC_COEX_SESSION_ID_DEFAULT); + break; } dw->nfcc_coex.prev_offset_sys_time = offset_sys_time; @@ -189,7 +215,7 @@ dw3000_nfcc_coex_header_check(struct dw3000 *dw, return -EINVAL; } /* Check AP_NFCC Interface Version ID. */ - if (msg->ver_id != DW3000_NFCC_COEX_VER_ID) { + if (msg->ver_id != dw->nfcc_coex.version) { return -EINVAL; } /* Read number of TLVs. */ diff --git a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_msg.h b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_msg.h index 001de1d..4214088 100644 --- a/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_msg.h +++ b/kernel/drivers/net/ieee802154/dw3000_nfcc_coex_msg.h @@ -28,6 +28,7 @@ #include "dw3000.h" #define TLV_MAX_NB_SLOTS 4 +#define DW3000_NFCC_COEX_SESSION_ID_DEFAULT 0 /** * enum dw3000_nfcc_coex_tlv_type - TLVs types. @@ -43,6 +44,8 @@ * Indicate error condition. * @DW3000_NFCC_COEX_TLV_TYPE_SLOT_LIST_UUS: * Indicate the UWB clock offset in V2 protocol. + * @DW3000_NFCC_COEX_TLV_TYPE_STOP_SESSION: + * Indicate the stop session in V3 protocol. */ enum dw3000_nfcc_coex_tlv_type { DW3000_NFCC_COEX_TLV_TYPE_UNSPEC, @@ -52,6 +55,7 @@ enum dw3000_nfcc_coex_tlv_type { DW3000_NFCC_COEX_TLV_TYPE_TLV_UWBCNT_OFFS, DW3000_NFCC_COEX_TLV_TYPE_ERROR, DW3000_NFCC_COEX_TLV_TYPE_SLOT_LIST_UUS, + DW3000_NFCC_COEX_TLV_TYPE_STOP_SESSION }; /** diff --git a/kernel/drivers/net/ieee802154/dw3000_pctt_mcps.c b/kernel/drivers/net/ieee802154/dw3000_pctt_mcps.c index 429fe52..400080c 100644 --- a/kernel/drivers/net/ieee802154/dw3000_pctt_mcps.c +++ b/kernel/drivers/net/ieee802154/dw3000_pctt_mcps.c @@ -29,7 +29,7 @@ int dw3000_pctt_vendor_cmd(struct dw3000 *dw, u32 vendor_id, u32 subcmd, void *data, size_t data_len) { - struct dw3000_vendor_cmd_pctt_setup_hw *info = data; + struct llhw_vendor_cmd_pctt_setup_hw *info = data; struct dw3000_config *config = &dw->config; int rc; @@ -62,5 +62,5 @@ int dw3000_pctt_vendor_cmd(struct dw3000 *dw, u32 vendor_id, u32 subcmd, return rc; } dw->pctt.enabled = !!info; - return 0; + return dw3000_enable_auto_fcs(dw, !dw->pctt.enabled); } diff --git a/kernel/drivers/net/ieee802154/dw3000_spi.c b/kernel/drivers/net/ieee802154/dw3000_spi.c index 30f0561..d710868 100644 --- a/kernel/drivers/net/ieee802154/dw3000_spi.c +++ b/kernel/drivers/net/ieee802154/dw3000_spi.c @@ -116,7 +116,7 @@ static int dw3000_spi_probe(struct spi_device *spi) hrtimer_init(&dw->idle_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); dw->idle_timer.function = dw3000_idle_timeout; - dev_info(dw->dev, "Loading driver...2022_03_04"); + dev_info(dw->dev, "Loading driver..."); dw3000_sysfs_init(dw); /* Setup SPI parameters */ @@ -141,16 +141,7 @@ static int dw3000_spi_probe(struct spi_device *spi) rc = spi_setup(spi); if (rc != 0) goto err_spi_setup; -#if (KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE) - /* Setup SPI controller CS timings */ - { - struct spi_delay dly = { .unit = SPI_DELAY_UNIT_NSECS, - .value = 0 }; - rc = spi_set_cs_timing(spi, &dly, &dly, &dly); - if (rc != 0) - goto err_spi_setup; - } -#endif + /* Request and setup regulators if availables*/ dw3000_setup_regulators(dw); @@ -185,13 +176,6 @@ static int dw3000_spi_probe(struct spi_device *spi) if (rc != 0) goto err_setup_irq; - /* Register MCPS 802.15.4 device */ - rc = dw3000_mcps_register(dw); - if (rc != 0) { - dev_err(&spi->dev, "could not register: %d\n", rc); - goto err_register_hw; - } - /* * Initialize PM QoS. Using the default latency won't change anything * to the QoS list @@ -208,17 +192,25 @@ static int dw3000_spi_probe(struct spi_device *spi) if (rc != 0) goto err_debugfs; + /* Register MCPS 802.15.4 device */ + rc = dw3000_mcps_register(dw); + if (rc != 0) { + dev_err(&spi->dev, "could not register: %d\n", rc); + goto err_register_hw; + } + /* All is ok */ return 0; +err_register_hw: + dw3000_debugfs_remove(dw); err_debugfs: err_state_start: dw3000_pm_qos_remove_request(dw); - dw3000_mcps_unregister(dw); -err_register_hw: err_setup_irq: dw3000_state_stop(dw); err_state_init: + dw3000_transfers_free(dw); err_transfers_init: err_setup_gpios: err_spi_setup: @@ -228,6 +220,7 @@ err_qos_latency: err_thread_cpu: err_wifi_coex: dw3000_mcps_free(dw); + spi_set_drvdata(spi, NULL); err_alloc_hw: return rc; } @@ -246,26 +239,24 @@ static int dw3000_spi_remove(struct spi_device *spi) { struct dw3000 *dw = spi_get_drvdata(spi); - dw3000_cir_data_alloc_count(dw, 0); - - dw3000_sysfs_remove(dw); - - dw3000_debugfs_remove(dw); + if (dw == NULL) + /* Error during probe, all already freed */ + return 0; dev_dbg(dw->dev, "unloading..."); + /* Remove sysfs files */ + dw3000_debugfs_remove(dw); + dw3000_sysfs_remove(dw); /* Unregister subsystems */ dw3000_mcps_unregister(dw); - - dw3000_pm_qos_remove_request(dw); - /* Stop state machine */ dw3000_state_stop(dw); - + dw3000_pm_qos_remove_request(dw); /* Free pre-computed SPI messages */ dw3000_transfers_free(dw); - /* Release the mcps 802.15.4 device */ + dw3000_cir_data_alloc_count(dw, 0); dw3000_mcps_free(dw); return 0; diff --git a/kernel/drivers/net/ieee802154/dw3000_stm.c b/kernel/drivers/net/ieee802154/dw3000_stm.c index 40da614..5eee5cf 100644 --- a/kernel/drivers/net/ieee802154/dw3000_stm.c +++ b/kernel/drivers/net/ieee802154/dw3000_stm.c @@ -75,7 +75,7 @@ int dw3000_enqueue_generic(struct dw3000 *dw, struct dw3000_stm_command *cmd) return cmd->cmd(dw, cmd->in, cmd->out); } - /* Mutex is used in dw3000_enqueue_generic() + /* Mutex is used in dw3000_enqueue_generic() * This protection will work with the spinlock in order to allow * the CPU to sleep and avoid ressources wasting during spinning */ @@ -302,8 +302,11 @@ int dw3000_state_init(struct dw3000 *dw, int cpu) stm->mthread = kthread_create(dw3000_event_thread, dw, "dw3000-%s", dev_name(dw->dev)); if (IS_ERR(stm->mthread)) { - return PTR_ERR(stm->mthread); + int err = PTR_ERR(stm->mthread); + stm->mthread = NULL; + return err; } + get_task_struct(stm->mthread); if (cpu >= 0) kthread_bind(stm->mthread, (unsigned)cpu); dw->dw3000_pid = stm->mthread->pid; @@ -360,8 +363,13 @@ int dw3000_state_stop(struct dw3000 *dw) { struct dw3000_state *stm = &dw->stm; + if (stm->mthread == NULL) + return 0; /* already stopped or not created yet */ + /* Stop state machine thread */ kthread_stop(stm->mthread); + put_task_struct(stm->mthread); + stm->mthread = NULL; dev_dbg(dw->dev, "state machine stopped\n"); return 0; diff --git a/kernel/drivers/net/ieee802154/dw3000_testmode.c b/kernel/drivers/net/ieee802154/dw3000_testmode.c index e5e2e3d..1097d2d 100644 --- a/kernel/drivers/net/ieee802154/dw3000_testmode.c +++ b/kernel/drivers/net/ieee802154/dw3000_testmode.c @@ -105,11 +105,17 @@ static int do_tm_cmd_get_rx_diag(struct dw3000 *dw, const void *in, void *out) { const struct do_tm_cmd_params *params = in; struct dw3000_stats *stats = &dw->stats; - size_t rssi_len = - stats->count[DW3000_STATS_RX_GOOD] * sizeof(struct dw3000_rssi); struct sk_buff *nl_skb; + size_t rssi_len; + int count = stats->count[DW3000_STATS_RX_GOOD]; int rc; + /* TODO: we don't send RSSI data for error frames. We should change this. */ + rssi_len = count < (DW3000_RSSI_REPORTS_MAX << 1) ? + count : + DW3000_RSSI_REPORTS_MAX << 1; + rssi_len *= sizeof(struct dw3000_rssi); + /** * Allocate netlink message. The approximated size includes * the testmode's command id and data. @@ -320,8 +326,8 @@ static int do_tm_cmd_stop_cont_tx(struct dw3000 *dw, const void *in, void *out) return dw3000_testmode_continuous_tx_stop(dw); } -static int do_tm_cmd_set_hrp_params(struct dw3000 *dw, const void *in, - void *out) +static int do_tm_cmd_set_hrp_uwb_params(struct dw3000 *dw, const void *in, + void *out) { const struct do_tm_cmd_params *params = in; u32 psr; @@ -398,7 +404,7 @@ int dw3000_tm_cmd(struct mcps802154_llhw *llhw, void *data, int len) [DW3000_TM_CMD_START_CONTINUOUS_TX] = do_tm_cmd_start_cont_tx, [DW3000_TM_CMD_STOP_CONTINUOUS_TX] = do_tm_cmd_stop_cont_tx, [DW3000_TM_CMD_DEEP_SLEEP] = do_tm_cmd_deep_sleep, - [DW3000_TM_CMD_SET_HRP_PARAMS] = do_tm_cmd_set_hrp_params, + [DW3000_TM_CMD_SET_HRP_PARAMS] = do_tm_cmd_set_hrp_uwb_params, [DW3000_TM_CMD_SET_CHANNEL] = do_tm_cmd_set_channel, }; u32 tm_cmd; diff --git a/kernel/drivers/net/ieee802154/dw3000_trc.h b/kernel/drivers/net/ieee802154/dw3000_trc.h index 268e7f6..29943e8 100644 --- a/kernel/drivers/net/ieee802154/dw3000_trc.h +++ b/kernel/drivers/net/ieee802154/dw3000_trc.h @@ -201,29 +201,30 @@ TRACE_DEFINE_ENUM(DW3000_PWR_TX); #define DW_SYS_STATUS_FLAGS_PR_ARG __entry->status #endif -#define RX_INFO_FLAGS_ENTRY __field(u8, flags) -#define RX_INFO_FLAGS_ASSIGN entry->flags = flags -#define RX_INFO_FLAGS_PR_FMT "flags: %s" +#define RX_FRAME_CONFIG_FLAGS_ENTRY __field(u8, flags) +#define RX_FRAME_CONFIG_FLAGS_ASSIGN entry->flags = flags +#define RX_FRAME_CONFIG_FLAGS_PR_FMT "flags: %s" -#define mcps802154_rx_info_name(name) \ - { \ - MCPS802154_RX_INFO_##name, #name \ +#define mcps802154_rx_frame_config_name(name) \ + { \ + MCPS802154_RX_FRAME_CONFIG_##name, #name \ } /* clang-format off */ -#define RX_INFO_FLAGS \ - mcps802154_rx_info_name(TIMESTAMP_DTU), \ - mcps802154_rx_info_name(AACK), \ - mcps802154_rx_info_name(RANGING), \ - mcps802154_rx_info_name(KEEP_RANGING_CLOCK), \ - mcps802154_rx_info_name(RANGING_PDOA), \ - mcps802154_rx_info_name(SP3), \ - mcps802154_rx_info_name(SP2), \ - mcps802154_rx_info_name(SP1), \ - mcps802154_rx_info_name(STS_MODE_MASK) +#define RX_FRAME_CONFIG_FLAGS \ + mcps802154_rx_frame_config_name(TIMESTAMP_DTU), \ + mcps802154_rx_frame_config_name(AACK), \ + mcps802154_rx_frame_config_name(RANGING), \ + mcps802154_rx_frame_config_name(KEEP_RANGING_CLOCK), \ + mcps802154_rx_frame_config_name(RANGING_PDOA), \ + mcps802154_rx_frame_config_name(SP3), \ + mcps802154_rx_frame_config_name(SP2), \ + mcps802154_rx_frame_config_name(SP1), \ + mcps802154_rx_frame_config_name(STS_MODE_MASK) /* clang-format on */ -#define RX_INFO_FLAGS_PR_ARG __print_flags(__entry->flags, "|", RX_INFO_FLAGS) +#define RX_FRAME_CONFIG_FLAGS_PR_ARG \ + __print_flags(__entry->flags, "|", RX_FRAME_CONFIG_FLAGS) #define RX_FRAME_INFO_FLAGS_ENTRY __field(u16, flags) #define RX_FRAME_INFO_FLAGS_ASSIGN entry->flags = flags @@ -252,29 +253,29 @@ TRACE_DEFINE_ENUM(DW3000_PWR_TX); #define RX_FRAME_INFO_FLAGS_PR_ARG \ __print_flags(__entry->flags, "|", RX_FRAME_INFO_FLAGS) -#define TX_FRAME_INFO_FLAGS_ENTRY __field(u8, flags) -#define TX_FRAME_INFO_FLAGS_ASSIGN entry->flags = flags -#define TX_FRAME_INFO_FLAGS_PR_FMT "flags: %s" +#define TX_FRAME_CONFIG_FLAGS_ENTRY __field(u8, flags) +#define TX_FRAME_CONFIG_FLAGS_ASSIGN entry->flags = flags +#define TX_FRAME_CONFIG_FLAGS_PR_FMT "flags: %s" -#define mcps802154_tx_frame_info_name(name) \ - { \ - MCPS802154_TX_FRAME_##name, #name \ +#define mcps802154_tx_frame_config_name(name) \ + { \ + MCPS802154_TX_FRAME_CONFIG_##name, #name \ } /* clang-format off */ -#define TX_FRAME_INFO_FLAGS \ - mcps802154_tx_frame_info_name(TIMESTAMP_DTU), \ - mcps802154_tx_frame_info_name(CCA), \ - mcps802154_tx_frame_info_name(RANGING), \ - mcps802154_tx_frame_info_name(KEEP_RANGING_CLOCK), \ - mcps802154_tx_frame_info_name(SP3), \ - mcps802154_tx_frame_info_name(SP2), \ - mcps802154_tx_frame_info_name(SP1), \ - mcps802154_tx_frame_info_name(STS_MODE_MASK) +#define TX_FRAME_CONFIG_FLAGS \ + mcps802154_tx_frame_config_name(TIMESTAMP_DTU), \ + mcps802154_tx_frame_config_name(CCA), \ + mcps802154_tx_frame_config_name(RANGING), \ + mcps802154_tx_frame_config_name(KEEP_RANGING_CLOCK), \ + mcps802154_tx_frame_config_name(SP3), \ + mcps802154_tx_frame_config_name(SP2), \ + mcps802154_tx_frame_config_name(SP1), \ + mcps802154_tx_frame_config_name(STS_MODE_MASK) /* clang-format on */ -#define TX_FRAME_INFO_FLAGS_PR_ARG \ - __print_flags(__entry->flags, "|", TX_FRAME_INFO_FLAGS) +#define TX_FRAME_CONFIG_FLAGS_PR_ARG \ + __print_flags(__entry->flags, "|", TX_FRAME_CONFIG_FLAGS) #define dw3000_nfcc_coex_tlv_type_name(name) \ { \ @@ -315,6 +316,26 @@ TRACE_DEFINE_ENUM(DW3000_NFCC_COEX_TLV_TYPE_SLOT_LIST_UUS); TRACE_DEFINE_ENUM(DW3000_DSS_STAT_SPI1_AVAIL_BIT_MASK); TRACE_DEFINE_ENUM(DW3000_DSS_STAT_SPI2_AVAIL_BIT_MASK); +#define dw3000_nfcc_coex_send_name(name) \ + { \ + DW3000_NFCC_COEX_SEND_##name, #name \ + } + +#define DW3000_NFCC_COEX_SEND_ENTRY __field(enum dw3000_nfcc_coex_send, send) +#define DW3000_NFCC_COEX_SEND_ASSIGN __entry->send = send +#define DW3000_NFCC_COEX_SEND_PR_FMT "send: %s" +/* clang-format off */ +#define DW3000_NFCC_COEX_SEND \ + dw3000_nfcc_coex_send_name(CLK_SYNC), \ + dw3000_nfcc_coex_send_name(CLK_OFFSET), \ + dw3000_nfcc_coex_send_name(STOP) +/* clang-format on */ +TRACE_DEFINE_ENUM(DW3000_NFCC_COEX_SEND_CLK_SYNC); +TRACE_DEFINE_ENUM(DW3000_NFCC_COEX_SEND_CLK_OFFSET); +TRACE_DEFINE_ENUM(DW3000_NFCC_COEX_SEND_STOP); +#define DW3000_NFCC_COEX_SEND_ARG \ + __print_symbolic(__entry->send, DW3000_NFCC_COEX_SEND) + /* We don't want clang-format to modify the following events definition! Look at net/wireless/trace.h for the required format. */ /* clang-format off */ @@ -407,16 +428,16 @@ TRACE_EVENT(dw3000_mcps_tx_frame, TP_ARGS(dw, flags, len), TP_STRUCT__entry( DW_ENTRY - TX_FRAME_INFO_FLAGS_ENTRY + TX_FRAME_CONFIG_FLAGS_ENTRY __field(u16, len) ), TP_fast_assign( DW_ASSIGN; - TX_FRAME_INFO_FLAGS_ASSIGN; + TX_FRAME_CONFIG_FLAGS_ASSIGN; __entry->len = len; ), - TP_printk(DW_PR_FMT ", " TX_FRAME_INFO_FLAGS_PR_FMT ", skb->len: %d", - DW_PR_ARG, TX_FRAME_INFO_FLAGS_PR_ARG,__entry->len) + TP_printk(DW_PR_FMT ", " TX_FRAME_CONFIG_FLAGS_PR_FMT ", skb->len: %d", + DW_PR_ARG, TX_FRAME_CONFIG_FLAGS_PR_ARG,__entry->len) ); TRACE_EVENT(dw3000_mcps_tx_frame_too_late, @@ -442,16 +463,16 @@ TRACE_EVENT(dw3000_mcps_rx_enable, TP_ARGS(dw, flags, timeout), TP_STRUCT__entry( DW_ENTRY - RX_INFO_FLAGS_ENTRY + RX_FRAME_CONFIG_FLAGS_ENTRY __field(int, timeout) ), TP_fast_assign( DW_ASSIGN; - RX_INFO_FLAGS_ASSIGN; + RX_FRAME_CONFIG_FLAGS_ASSIGN; __entry->timeout = timeout; ), - TP_printk(DW_PR_FMT ", " RX_INFO_FLAGS_PR_FMT ", timeout: %d", - DW_PR_ARG, RX_INFO_FLAGS_PR_ARG, __entry->timeout) + TP_printk(DW_PR_FMT ", " RX_FRAME_CONFIG_FLAGS_PR_FMT ", timeout: %d", + DW_PR_ARG, RX_FRAME_CONFIG_FLAGS_PR_ARG, __entry->timeout) ); TRACE_EVENT(dw3000_mcps_rx_enable_too_late, @@ -521,6 +542,11 @@ DEFINE_EVENT(dw_only_evt, dw3000_wakeup_done_to_idle, TP_ARGS(dw) ); +DEFINE_EVENT(dw_only_evt, dw3000_wakeup_done_to_idle_late, + TP_PROTO(struct dw3000 *dw), + TP_ARGS(dw) +); + TRACE_EVENT(dw3000_idle, TP_PROTO(struct dw3000 *dw, bool timeout, u32 timeout_dtu, enum operational_state next_operational_state), @@ -1172,6 +1198,21 @@ TRACE_EVENT(dw3000_nfcc_coex_clock_offset_payload_put, __entry->clock_offset_sys_time) ); +TRACE_EVENT(dw3000_nfcc_coex_stop_session_payload_put, + TP_PROTO(struct dw3000 *dw, u32 session_id), + TP_ARGS(dw, session_id), + TP_STRUCT__entry( + DW_ENTRY + __field(u32, session_id) + ), + TP_fast_assign( + DW_ASSIGN; + __entry->session_id = session_id; + ), + TP_printk(DW_PR_FMT ", session_id %d", DW_PR_ARG, + __entry->session_id) +); + TRACE_EVENT(dw3000_nfcc_coex_rx_msg_info, TP_PROTO(struct dw3000 *dw, u32 next_timestamp_dtu, int next_duration_dtu), @@ -1238,7 +1279,7 @@ TRACE_EVENT(dw3000_nfcc_coex_tlv_check, ); TRACE_EVENT(dw3000_nfcc_coex_handle_access, - TP_PROTO(struct dw3000 *dw, const struct dw3000_vendor_cmd_nfcc_coex_handle_access *info, + TP_PROTO(struct dw3000 *dw, const struct llhw_vendor_cmd_nfcc_coex_handle_access *info, s32 idle_duration_dtu), TP_ARGS(dw, info, idle_duration_dtu), TP_STRUCT__entry( @@ -1266,6 +1307,30 @@ TRACE_EVENT(dw3000_nfcc_coex_handle_access, __entry->duration_dtu, __entry->chan) ); +TRACE_EVENT(dw3000_nfcc_coex_wakeup_and_send, + TP_PROTO(struct dw3000 *dw, enum dw3000_nfcc_coex_send send, + s32 idle_duration_dtu, u32 send_timestamp_dtu), + TP_ARGS(dw, send, idle_duration_dtu, send_timestamp_dtu), + TP_STRUCT__entry( + DW_ENTRY + DW3000_NFCC_COEX_SEND_ENTRY + __field(s32, idle_duration_dtu) + __field(u32, send_timestamp_dtu) + ), + TP_fast_assign( + DW_ASSIGN; + DW3000_NFCC_COEX_SEND_ASSIGN, + __entry->idle_duration_dtu = idle_duration_dtu; + __entry->send_timestamp_dtu = send_timestamp_dtu; + ), + TP_printk(DW_PR_FMT ", " DW3000_NFCC_COEX_SEND_PR_FMT + ", idle_duration_dtu: %d, send_timestamp_dtu: 0x%08x", + DW_PR_ARG, + DW3000_NFCC_COEX_SEND_ARG, + __entry->idle_duration_dtu, + __entry->send_timestamp_dtu) +); + TRACE_EVENT(dw3000_nfcc_coex_err, TP_PROTO(struct dw3000 *dw, const char *err), TP_ARGS(dw, err), @@ -1436,6 +1501,30 @@ TRACE_EVENT(dw3000_read_clockoffset, __entry->cfo) ); +TRACE_EVENT(dw3000_nfcc_coex_prepare_config, + TP_PROTO(struct dw3000 *dw), + TP_ARGS(dw), + TP_STRUCT__entry( + DW_ENTRY + ), + TP_fast_assign( + DW_ASSIGN; + ), + TP_printk(DW_PR_FMT, DW_PR_ARG) + ); + +TRACE_EVENT(dw3000_nfcc_coex_restore_config, + TP_PROTO(struct dw3000 *dw), + TP_ARGS(dw), + TP_STRUCT__entry( + DW_ENTRY + ), + TP_fast_assign( + DW_ASSIGN; + ), + TP_printk(DW_PR_FMT, DW_PR_ARG) + ); + /* clang-format on */ #endif /* !__DW3000_TRACE || TRACE_HEADER_MULTI_READ */ diff --git a/kernel/drivers/net/ieee802154/dw3000_txpower_adjustment.c b/kernel/drivers/net/ieee802154/dw3000_txpower_adjustment.c index d80e854..1b2ae14 100644 --- a/kernel/drivers/net/ieee802154/dw3000_txpower_adjustment.c +++ b/kernel/drivers/net/ieee802154/dw3000_txpower_adjustment.c @@ -325,6 +325,7 @@ static u32 adjust_tx_power(u16 frame_duration_us, u32 ref_tx_power, u8 channel, { u32 adjusted_tx_power; u16 target_boost = 0; + u16 base_target_boost = 0; u16 current_boost = 0; u16 best_boost_abs = 0; u16 best_boost = 0; @@ -332,7 +333,7 @@ static u32 adjust_tx_power(u16 frame_duration_us, u32 ref_tx_power, u8 channel, u16 lower_limit = 0; const u8 *lut = NULL; - uint8_t ref_tx_power_byte[4]; /* txpwr of each segments (UM 8.2.2.20) */ + uint8_t ref_tx_power_byte[4]; /* txpwr of each segment (UM 8.2.2.20) */ uint8_t adj_tx_power_byte[4]; uint8_t adj_tx_power_boost[4]; u8 best_index; @@ -341,29 +342,31 @@ static u32 adjust_tx_power(u16 frame_duration_us, u32 ref_tx_power, u8 channel, u8 ref_fine_gain; bool within_margin_flag; bool reached_max_fine_gain_flag; + bool shortcut_optim_flag; u8 unlock; - u8 i; + u8 i, j; int k; int ret = 0; - target_boost = calculate_power_boost(frame_duration_us); + base_target_boost = calculate_power_boost(frame_duration_us); if (th_boost) { - *th_boost = target_boost; + *th_boost = base_target_boost; } switch (channel) { case 5: lut = fine_gain_lut_chan5; - if (target_boost >= MAX_BOOST_CH5) - target_boost = MAX_BOOST_CH5; + if (base_target_boost > MAX_BOOST_CH5) + base_target_boost = MAX_BOOST_CH5; break; default: lut = fine_gain_lut_chan9; - if (target_boost >= MAX_BOOST_CH9) - target_boost = MAX_BOOST_CH9; + if (base_target_boost > MAX_BOOST_CH9) + base_target_boost = MAX_BOOST_CH9; break; } for (k = 0; k < 4; k++) { + target_boost = base_target_boost; current_boost = 0; best_boost_abs = 0; best_boost = 0; @@ -371,6 +374,7 @@ static u32 adjust_tx_power(u16 frame_duration_us, u32 ref_tx_power, u8 channel, best_coarse_gain = 0; within_margin_flag = false; reached_max_fine_gain_flag = false; + shortcut_optim_flag = false; unlock = 0; i = 0; ref_tx_power_byte[k] = (u8)(ref_tx_power >> (k << 3)); @@ -382,10 +386,14 @@ static u32 adjust_tx_power(u16 frame_duration_us, u32 ref_tx_power, u8 channel, i = ref_fine_gain; /* Avoid re-doing the same math four times */ - if (k > 0 && (ref_tx_power_byte[k] == ref_tx_power_byte[0])) { - adj_tx_power_byte[k] = adj_tx_power_byte[0]; - continue; + for (j = 0; !shortcut_optim_flag && (j < k); j++) { + if (ref_tx_power_byte[k] == ref_tx_power_byte[j]) { + adj_tx_power_byte[k] = adj_tx_power_byte[j]; + shortcut_optim_flag = true; + } } + if (shortcut_optim_flag) + continue; /* PHR power must be 6dB lower than PSDU */ if (k == 1) { @@ -458,8 +466,7 @@ static u32 adjust_tx_power(u16 frame_duration_us, u32 ref_tx_power, u8 channel, /* Corner case: when fine gain setting is very low, it can happened that * current boost is already larger than target_boost but not within margin. - * Then, just return current solution. - */ + * Then, just return current solution. */ if (current_boost >= upper_limit && !reached_max_fine_gain_flag) { break; diff --git a/kernel/drivers/net/ieee802154/mcps802154_fake.c b/kernel/drivers/net/ieee802154/mcps802154_fake.c index ae8dcad..082b20b 100644 --- a/kernel/drivers/net/ieee802154/mcps802154_fake.c +++ b/kernel/drivers/net/ieee802154/mcps802154_fake.c @@ -75,8 +75,8 @@ static void stop(struct mcps802154_llhw *llhw) } static int tx_frame(struct mcps802154_llhw *llhw, struct sk_buff *skb, - const struct mcps802154_tx_frame_info *info, int frame_idx, - int next_delay_dtu) + const struct mcps802154_tx_frame_config *config, + int frame_idx, int next_delay_dtu) { if (!started) { pr_err("fake_mcps: %s called and not started\n", __func__); @@ -90,8 +90,8 @@ static int tx_frame(struct mcps802154_llhw *llhw, struct sk_buff *skb, } static int rx_enable(struct mcps802154_llhw *llhw, - const struct mcps802154_rx_info *info, int frame_idx, - int next_delay_dtu) + const struct mcps802154_rx_frame_config *info, + int frame_idx, int next_delay_dtu) { if (!started) { pr_err("fake_mcps: %s called and not started\n", __func__); @@ -238,9 +238,10 @@ static int get_current_timestamp_dtu(struct mcps802154_llhw *llhw, return 0; } -static u64 tx_timestamp_dtu_to_rmarker_rctu(struct mcps802154_llhw *llhw, - u32 tx_timestamp_dtu, - int ant_set_id) +static u64 tx_timestamp_dtu_to_rmarker_rctu( + struct mcps802154_llhw *llhw, u32 tx_timestamp_dtu, + const struct mcps802154_hrp_uwb_params *hrp_uwb_params, + const struct mcps802154_channel *channel_params, int ant_set_id) { if (!started) { pr_err("fake_mcps: %s called and not started\n", __func__); @@ -281,8 +282,8 @@ static int set_channel(struct mcps802154_llhw *llhw, u8 page, u8 channel, return 0; } -static int set_hrp_uwb_params(struct mcps802154_llhw *llhw, int prf, int psr, - int sfd_selector, int phr_rate, int data_rate) +static int set_hrp_uwb_params(struct mcps802154_llhw *llhw, + const struct mcps802154_hrp_uwb_params *params) { if (!started) { pr_err("fake_mcps: %s called and not started\n", __func__); @@ -434,6 +435,17 @@ static int __init fake_init(void) driver_llhw->hw->flags = (IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT | IEEE802154_HW_PROMISCUOUS | IEEE802154_HW_RX_OMIT_CKSUM); + driver_llhw->flags = + (MCPS802154_LLHW_BPRF | MCPS802154_LLHW_DATA_RATE_6M81 | + MCPS802154_LLHW_PHR_DATA_RATE_850K | + MCPS802154_LLHW_PHR_DATA_RATE_6M81 | MCPS802154_LLHW_PRF_16 | + MCPS802154_LLHW_PRF_64 | MCPS802154_LLHW_PSR_32 | + MCPS802154_LLHW_PSR_64 | MCPS802154_LLHW_PSR_128 | + MCPS802154_LLHW_PSR_256 | MCPS802154_LLHW_PSR_1024 | + MCPS802154_LLHW_PSR_4096 | MCPS802154_LLHW_SFD_4A | + MCPS802154_LLHW_SFD_4Z_8 | MCPS802154_LLHW_STS_SEGMENT_1 | + MCPS802154_LLHW_AOA_AZIMUTH | MCPS802154_LLHW_AOA_ELEVATION | + MCPS802154_LLHW_AOA_FOM); /* UWB High band 802.15.4a-2007. */ driver_llhw->hw->phy->supported.channels[4] |= 0xffe0; diff --git a/kernel/include/net/idle_region_nl.h b/kernel/include/net/idle_region_nl.h new file mode 120000 index 0000000..d1e5a9c --- /dev/null +++ b/kernel/include/net/idle_region_nl.h @@ -0,0 +1 @@ +../../../mac/include/net/idle_region_nl.h
\ No newline at end of file diff --git a/kernel/include/net/mcps_skb_frag.h b/kernel/include/net/mcps_skb_frag.h new file mode 120000 index 0000000..55fa1c3 --- /dev/null +++ b/kernel/include/net/mcps_skb_frag.h @@ -0,0 +1 @@ +../../../mac/include/net/mcps_skb_frag.h
\ No newline at end of file diff --git a/kernel/include/net/simple_ranging_region_nl.h b/kernel/include/net/simple_ranging_region_nl.h deleted file mode 120000 index 79bdc0f..0000000 --- a/kernel/include/net/simple_ranging_region_nl.h +++ /dev/null @@ -1 +0,0 @@ -../../../mac/include/net/simple_ranging_region_nl.h
\ No newline at end of file diff --git a/kernel/net/mcps802154/Kbuild b/kernel/net/mcps802154/Kbuild index f6ba8c8..f032cd7 100644 --- a/kernel/net/mcps802154/Kbuild +++ b/kernel/net/mcps802154/Kbuild @@ -1,4 +1,8 @@ -obj-m := mcps802154.o mcps802154_region_fira.o \ +ifndef CONFIG_MCPS802154 +CONFIG_MCPS802154:=m +endif + +obj-$(CONFIG_MCPS802154) := mcps802154.o mcps802154_region_fira.o \ mcps802154_region_nfcc_coex.o \ mcps802154_region_pctt.o \ @@ -9,27 +13,27 @@ mcps802154-y := \ default_region.o \ endless_scheduler.o \ on_demand_scheduler.o \ + idle_region.o \ fproc.o \ fproc_broken.o \ fproc_multi.o \ fproc_vendor.o \ fproc_nothing.o \ + fproc_idle.o \ fproc_rx.o \ fproc_stopped.o \ fproc_tx.o \ frame.o \ ie.o \ mcps_main.o \ + mcps_skb_frag.o \ nl.o \ ops.o \ regions.o \ - simple_ranging_region.o \ schedule.o \ schedulers.o \ trace.o -mcps802154-$(CONFIG_MCPS802154_TESTMODE) += ping_pong_region.o - mcps802154_region_fira-y := \ fira_access.o \ fira_aead.o \ @@ -41,6 +45,10 @@ mcps802154_region_fira-y := \ fira_region.o \ fira_region_call.o \ fira_session.o \ + fira_session_fsm.o \ + fira_session_fsm_init.o \ + fira_session_fsm_idle.o \ + fira_session_fsm_active.o \ fira_trace.o mcps802154_region_nfcc_coex-y := \ diff --git a/kernel/net/mcps802154/fira_round_hopping_crypto.c b/kernel/net/mcps802154/fira_round_hopping_crypto.c index 2728b21..efb8259 100644 --- a/kernel/net/mcps802154/fira_round_hopping_crypto.c +++ b/kernel/net/mcps802154/fira_round_hopping_crypto.c @@ -27,7 +27,7 @@ #include <linux/scatterlist.h> int fira_round_hopping_crypto_encrypt( - struct fira_round_hopping_sequence *round_hopping_sequence, + const struct fira_round_hopping_sequence *round_hopping_sequence, const u8 *data, u8 *out) { struct scatterlist sg; diff --git a/kernel/net/mcps802154/fira_session_fsm.c b/kernel/net/mcps802154/fira_session_fsm.c new file mode 120000 index 0000000..b815ae3 --- /dev/null +++ b/kernel/net/mcps802154/fira_session_fsm.c @@ -0,0 +1 @@ +../../../mac/fira_session_fsm.c
\ No newline at end of file diff --git a/kernel/net/mcps802154/fira_session_fsm.h b/kernel/net/mcps802154/fira_session_fsm.h new file mode 120000 index 0000000..5506507 --- /dev/null +++ b/kernel/net/mcps802154/fira_session_fsm.h @@ -0,0 +1 @@ +../../../mac/fira_session_fsm.h
\ No newline at end of file diff --git a/kernel/net/mcps802154/fira_session_fsm_active.c b/kernel/net/mcps802154/fira_session_fsm_active.c new file mode 120000 index 0000000..63f915b --- /dev/null +++ b/kernel/net/mcps802154/fira_session_fsm_active.c @@ -0,0 +1 @@ +../../../mac/fira_session_fsm_active.c
\ No newline at end of file diff --git a/kernel/net/mcps802154/fira_session_fsm_active.h b/kernel/net/mcps802154/fira_session_fsm_active.h new file mode 120000 index 0000000..7654f0a --- /dev/null +++ b/kernel/net/mcps802154/fira_session_fsm_active.h @@ -0,0 +1 @@ +../../../mac/fira_session_fsm_active.h
\ No newline at end of file diff --git a/kernel/net/mcps802154/fira_session_fsm_idle.c b/kernel/net/mcps802154/fira_session_fsm_idle.c new file mode 120000 index 0000000..4725342 --- /dev/null +++ b/kernel/net/mcps802154/fira_session_fsm_idle.c @@ -0,0 +1 @@ +../../../mac/fira_session_fsm_idle.c
\ No newline at end of file diff --git a/kernel/net/mcps802154/fira_session_fsm_idle.h b/kernel/net/mcps802154/fira_session_fsm_idle.h new file mode 120000 index 0000000..5182a72 --- /dev/null +++ b/kernel/net/mcps802154/fira_session_fsm_idle.h @@ -0,0 +1 @@ +../../../mac/fira_session_fsm_idle.h
\ No newline at end of file diff --git a/kernel/net/mcps802154/fira_session_fsm_init.c b/kernel/net/mcps802154/fira_session_fsm_init.c new file mode 120000 index 0000000..41149be --- /dev/null +++ b/kernel/net/mcps802154/fira_session_fsm_init.c @@ -0,0 +1 @@ +../../../mac/fira_session_fsm_init.c
\ No newline at end of file diff --git a/kernel/net/mcps802154/fira_session_fsm_init.h b/kernel/net/mcps802154/fira_session_fsm_init.h new file mode 120000 index 0000000..f520adb --- /dev/null +++ b/kernel/net/mcps802154/fira_session_fsm_init.h @@ -0,0 +1 @@ +../../../mac/fira_session_fsm_init.h
\ No newline at end of file diff --git a/kernel/net/mcps802154/fproc_idle.c b/kernel/net/mcps802154/fproc_idle.c new file mode 120000 index 0000000..430ae47 --- /dev/null +++ b/kernel/net/mcps802154/fproc_idle.c @@ -0,0 +1 @@ +../../../mac/fproc_idle.c
\ No newline at end of file diff --git a/kernel/net/mcps802154/idle_region.c b/kernel/net/mcps802154/idle_region.c new file mode 120000 index 0000000..6e6ca32 --- /dev/null +++ b/kernel/net/mcps802154/idle_region.c @@ -0,0 +1 @@ +../../../mac/idle_region.c
\ No newline at end of file diff --git a/kernel/net/mcps802154/idle_region.h b/kernel/net/mcps802154/idle_region.h new file mode 120000 index 0000000..fe7992b --- /dev/null +++ b/kernel/net/mcps802154/idle_region.h @@ -0,0 +1 @@ +../../../mac/idle_region.h
\ No newline at end of file diff --git a/kernel/net/mcps802154/mcps_skb_frag.c b/kernel/net/mcps802154/mcps_skb_frag.c new file mode 120000 index 0000000..cfdbfe1 --- /dev/null +++ b/kernel/net/mcps802154/mcps_skb_frag.c @@ -0,0 +1 @@ +../../../mac/mcps_skb_frag.c
\ No newline at end of file diff --git a/kernel/net/mcps802154/nl.c b/kernel/net/mcps802154/nl.c index d04eee6..80e520b 100644 --- a/kernel/net/mcps802154/nl.c +++ b/kernel/net/mcps802154/nl.c @@ -40,9 +40,6 @@ #define nla_strscpy nla_strlcpy #endif -/* Used to report ranging result, this should later be different per device. */ -static u32 ranging_report_portid; - static struct genl_family mcps802154_nl_family; static const struct nla_policy @@ -62,14 +59,6 @@ static const struct nla_policy [MCPS802154_REGION_ATTR_CALL_PARAMS] = { .type = NLA_NESTED }, }; -static const struct nla_policy mcps802154_nl_ranging_request_policy - [MCPS802154_RANGING_REQUEST_ATTR_MAX + 1] = { - [MCPS802154_RANGING_REQUEST_ATTR_ID] = { .type = NLA_U32 }, - [MCPS802154_RANGING_REQUEST_ATTR_FREQUENCY_HZ] = { .type = NLA_U32 }, - [MCPS802154_RANGING_REQUEST_ATTR_PEER] = { .type = NLA_U64 }, - [MCPS802154_RANGING_REQUEST_ATTR_REMOTE_PEER] = { .type = NLA_U64 }, - }; - static const struct nla_policy mcps802154_nl_policy[MCPS802154_ATTR_MAX + 1] = { [MCPS802154_ATTR_HW] = { .type = NLA_U32 }, [MCPS802154_ATTR_WPAN_PHY_NAME] = ATTR_STRING_POLICY, @@ -81,12 +70,11 @@ static const struct nla_policy mcps802154_nl_policy[MCPS802154_ATTR_MAX + 1] = { [MCPS802154_ATTR_SCHEDULER_CALL_PARAMS] = { .type = NLA_NESTED }, [MCPS802154_ATTR_SCHEDULER_REGION_CALL] = { .type = NLA_NESTED }, [MCPS802154_ATTR_CALIBRATIONS] = { .type = NLA_NESTED }, + [MCPS802154_ATTR_PWR_STATS] = { .type = NLA_NESTED }, #ifdef CONFIG_MCPS802154_TESTMODE [MCPS802154_ATTR_TESTDATA] = { .type = NLA_NESTED }, #endif - [MCPS802154_ATTR_RANGING_REQUESTS] = - NLA_POLICY_NESTED_ARRAY(mcps802154_nl_ranging_request_policy), }; /** @@ -403,6 +391,26 @@ static int mcps802154_nl_call_region(struct sk_buff *skb, return r; } +/** + * mcps802154_nl_close_scheduler() - Close current scheduler and its regions. + * @skb: Request message. + * @info: Request information. + * + * Return: 0 or error. + */ +static int mcps802154_nl_close_scheduler(struct sk_buff *skb, + struct genl_info *info) +{ + struct mcps802154_local *local = info->user_ptr[0]; + mutex_lock(&local->fsm_lock); + local->cur_cmd_info = info; + mcps802154_ca_close(local); + local->cur_cmd_info = NULL; + mutex_unlock(&local->fsm_lock); + + return 0; +} + struct sk_buff * mcps802154_region_call_alloc_reply_skb(struct mcps802154_llhw *llhw, struct mcps802154_region *region, @@ -634,243 +642,9 @@ int mcps802154_testmode_reply(struct mcps802154_llhw *llhw, struct sk_buff *skb) return genlmsg_reply(skb, local->cur_cmd_info); } EXPORT_SYMBOL(mcps802154_testmode_reply); - -/** - * mcps802154_nl_send_ping_pong_report() - Append ping_pong result to a netlink - * message. - * @local: MCPS private data. - * @msg: Message to write to. - * @portid: Destination port address. - * @id: ping_pong identifier. - * @t_0: t_0 of ping pong - * @t_3: t_3 of ping pong - * @t_4: t_4 of ping pong - * - * Return: 0 or error. - */ -static int mcps802154_nl_send_ping_pong_report(struct mcps802154_local *local, - struct sk_buff *msg, u32 portid, - int id, u64 t_0, u64 t_3, - u64 t_4) -{ - void *hdr; - struct nlattr *result; - - hdr = genlmsg_put(msg, ranging_report_portid, 0, &mcps802154_nl_family, - 0, MCPS802154_CMD_PING_PONG_REPORT); - if (!hdr) - return -ENOBUFS; - - if (nla_put_u32(msg, MCPS802154_ATTR_HW, local->hw_idx)) - goto error; - - result = nla_nest_start(msg, MCPS802154_ATTR_PING_PONG_RESULT); - if (!result) - goto error; - - if (nla_put_u32(msg, MCPS802154_PING_PONG_RESULT_ATTR_ID, id) || - nla_put_u64_64bit(msg, MCPS802154_PING_PONG_RESULT_ATTR_T_0, t_0, - 0) || - nla_put_u64_64bit(msg, MCPS802154_PING_PONG_RESULT_ATTR_T_3, t_3, - 0) || - nla_put_u64_64bit(msg, MCPS802154_PING_PONG_RESULT_ATTR_T_4, t_4, - 0)) - goto error; - - nla_nest_end(msg, result); - - genlmsg_end(msg, hdr); - return 0; -error: - genlmsg_cancel(msg, hdr); - return -EMSGSIZE; -} - -int mcps802154_nl_ping_pong_report(struct mcps802154_llhw *llhw, int id, - u64 t_0, u64 t_3, u64 t_4) -{ - struct mcps802154_local *local = llhw_to_local(llhw); - struct sk_buff *msg; - int r; - if (ranging_report_portid == 0) - return -ECONNREFUSED; - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - if (mcps802154_nl_send_ping_pong_report( - local, msg, ranging_report_portid, id, t_0, t_3, t_4)) { - nlmsg_free(msg); - return -ENOBUFS; - } - - r = genlmsg_unicast(wpan_phy_net(local->hw->phy), msg, - ranging_report_portid); - if (r == -ECONNREFUSED) { - ranging_report_portid = 0; - } - return r; -} #endif /** - * mcps802154_nl_set_ranging_requests() - Set ranging requests for a device. - * @skb: Request message. - * @info: Request information. - * - * Return: 0 or error. - */ -static int mcps802154_nl_set_ranging_requests(struct sk_buff *skb, - struct genl_info *info) -{ - struct mcps802154_local *local = info->user_ptr[0]; - struct nlattr *request; - struct nlattr *attrs[MCPS802154_RANGING_REQUEST_ATTR_MAX + 1]; - struct mcps802154_nl_ranging_request - requests[MCPS802154_NL_RANGING_REQUESTS_MAX]; - unsigned int n_requests = 0; - int r, rem; - - if (!local->ca.scheduler || !local->ca.scheduler->ops->ranging_setup) - return -EOPNOTSUPP; - - if (!info->attrs[MCPS802154_ATTR_RANGING_REQUESTS]) - return -EINVAL; - - nla_for_each_nested ( - request, info->attrs[MCPS802154_ATTR_RANGING_REQUESTS], rem) { - if (n_requests >= MCPS802154_NL_RANGING_REQUESTS_MAX) - return -EINVAL; - - r = nla_parse_nested(attrs, MCPS802154_RANGING_REQUEST_ATTR_MAX, - request, - mcps802154_nl_ranging_request_policy, - info->extack); - if (r) - return r; - - if (!attrs[MCPS802154_RANGING_REQUEST_ATTR_ID] || - !attrs[MCPS802154_RANGING_REQUEST_ATTR_FREQUENCY_HZ] || - !attrs[MCPS802154_RANGING_REQUEST_ATTR_PEER]) - return -EINVAL; - - requests[n_requests].id = - nla_get_s32(attrs[MCPS802154_RANGING_REQUEST_ATTR_ID]); - requests[n_requests].frequency_hz = nla_get_s32( - attrs[MCPS802154_RANGING_REQUEST_ATTR_FREQUENCY_HZ]); - requests[n_requests].peer_extended_addr = nla_get_le64( - attrs[MCPS802154_RANGING_REQUEST_ATTR_PEER]); - requests[n_requests].remote_peer_extended_addr = 0; - - if (attrs[MCPS802154_RANGING_REQUEST_ATTR_REMOTE_PEER]) - requests[n_requests] - .remote_peer_extended_addr = nla_get_le64( - attrs[MCPS802154_RANGING_REQUEST_ATTR_REMOTE_PEER]); - - n_requests++; - } - - mutex_lock(&local->fsm_lock); - r = local->ca.scheduler->ops->ranging_setup(local->ca.scheduler, - requests, n_requests); - mutex_unlock(&local->fsm_lock); - if (r) - return r; - - /* TODO: store per device. */ - ranging_report_portid = info->snd_portid; - - return 0; -} - -/** - * mcps802154_nl_send_ranging_report() - Append ranging result to a netlink - * message. - * @local: MCPS private data. - * @msg: Message to write to. - * @portid: Destination port address. - * @id: Ranging identifier. - * @report: Phase Differences Of Arrival and Time of Flight. - * - * Return: 0 or error. - */ -static int mcps802154_nl_send_ranging_report( - struct mcps802154_local *local, struct sk_buff *msg, u32 portid, int id, - const struct mcps802154_nl_ranging_report *report) -{ - void *hdr; - struct nlattr *result; - - hdr = genlmsg_put(msg, ranging_report_portid, 0, &mcps802154_nl_family, - 0, MCPS802154_CMD_RANGING_REPORT); - if (!hdr) - return -ENOBUFS; - - if (nla_put_u32(msg, MCPS802154_ATTR_HW, local->hw_idx)) - goto error; - - result = nla_nest_start(msg, MCPS802154_ATTR_RANGING_RESULT); - if (!result) - goto error; - - if (nla_put_u32(msg, MCPS802154_RANGING_RESULT_ATTR_ID, id) || - nla_put_s32(msg, MCPS802154_RANGING_RESULT_ATTR_TOF_RCTU, - report->tof_rctu) || - nla_put_s32(msg, MCPS802154_RANGING_RESULT_ATTR_LOCAL_PDOA_RAD_Q11, - report->local_pdoa_rad_q11) || - nla_put_s32(msg, MCPS802154_RANGING_RESULT_ATTR_REMOTE_PDOA_RAD_Q11, - report->remote_pdoa_rad_q11)) - goto error; - if (!report->is_same_rx_ant_set_id) { - if ((nla_put_s32( - msg, - MCPS802154_RANGING_RESULT_ATTR_LOCAL_PDOA_ELEVATION_RAD_Q11, - report->local_pdoa_elevation_rad_q11) || - nla_put_s32( - msg, - MCPS802154_RANGING_RESULT_ATTR_REMOTE_PDOA_ELEVATION_RAD_Q11, - report->remote_pdoa_elevation_rad_q11))) - goto error; - } - - nla_nest_end(msg, result); - genlmsg_end(msg, hdr); - return 0; -error: - genlmsg_cancel(msg, hdr); - return -EMSGSIZE; -} - -int mcps802154_nl_ranging_report( - struct mcps802154_llhw *llhw, int id, - const struct mcps802154_nl_ranging_report *report) -{ - struct mcps802154_local *local = llhw_to_local(llhw); - struct sk_buff *msg; - int r; - - if (ranging_report_portid == 0) - return -ECONNREFUSED; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - if (mcps802154_nl_send_ranging_report(local, msg, ranging_report_portid, - id, report)) { - nlmsg_free(msg); - return -ENOBUFS; - } - - r = genlmsg_unicast(wpan_phy_net(local->hw->phy), msg, - ranging_report_portid); - if (r == -ECONNREFUSED) - ranging_report_portid = 0; - - return r; -} - -/** * mcps802154_nl_put_calibration() - put on calibration in msg. * @msg: Request message. * @key: calibration name @@ -1161,6 +935,130 @@ failure: return err; } +/** + * mcps802154_nl_put_pwr_stats_state() - Put a power statistic state on a netlink message. + * @msg: Netlink message. + * @state: Related power statistic state. + * @time: Duration of this state. + * @count: Transitions count to this state. + * + * Return: 0 or error. + */ +static int +mcps802154_nl_put_pwr_stats_state(struct sk_buff *msg, + enum mcps802154_pwr_stats_attrs state, + u32 time, u32 count) +{ + struct nlattr *nl_pwr_stats_state; + + nl_pwr_stats_state = nla_nest_start(msg, state); + if (!nl_pwr_stats_state) + return -EMSGSIZE; + if (nla_put_u32(msg, MCPS802154_PWR_STATS_STATE_ATTR_TIME, time)) + return -EMSGSIZE; + if (nla_put_u32(msg, MCPS802154_PWR_STATS_STATE_ATTR_COUNT, count)) + return -EMSGSIZE; + nla_nest_end(msg, nl_pwr_stats_state); + return 0; +} + +/** + * mcps802154_nl_get_pwr_stats() - Get power statistics. + * @skb: Request message. + * @info: Request information. + * + * Return: 0 or error. + */ +static int mcps802154_nl_get_pwr_stats(struct sk_buff *skb, + struct genl_info *info) +{ + struct mcps802154_local *local = info->user_ptr[0]; + struct mcps802154_llhw *llhw = &local->llhw; + struct sk_buff *msg; + void *hdr; + struct mcps802154_power_stats pwr_stats; + struct nlattr *nl_pwr_stats; + int rc; + + if (!local->ops->get_power_stats) + return -EOPNOTSUPP; + + /* Get the power statistics from the low level hardware driver. */ + rc = local->ops->get_power_stats(llhw, &pwr_stats); + if (rc) + return rc; + + /* Build the response netlink message. */ + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, + &mcps802154_nl_family, 0, + MCPS802154_CMD_GET_PWR_STATS); + if (!hdr) { + rc = -ENOBUFS; + goto failure; + } + + nl_pwr_stats = nla_nest_start(msg, MCPS802154_ATTR_PWR_STATS); + if (!nl_pwr_stats) + goto nla_put_failure; + + /* Process the SLEEP state. */ + rc = mcps802154_nl_put_pwr_stats_state( + msg, MCPS802154_PWR_STATS_ATTR_SLEEP, + pwr_stats.power_state_stats[MCPS802154_PWR_STATE_SLEEP].dur / + 1000000, + pwr_stats.power_state_stats[MCPS802154_PWR_STATE_SLEEP].count); + if (rc) + goto nla_put_failure; + + /* Process the IDLE state. */ + rc = mcps802154_nl_put_pwr_stats_state( + msg, MCPS802154_PWR_STATS_ATTR_IDLE, + pwr_stats.power_state_stats[MCPS802154_PWR_STATE_IDLE].dur / + 1000000, + pwr_stats.power_state_stats[MCPS802154_PWR_STATE_IDLE].count); + if (rc) + goto nla_put_failure; + + /* Process the RX state. */ + rc = mcps802154_nl_put_pwr_stats_state( + msg, MCPS802154_PWR_STATS_ATTR_RX, + pwr_stats.power_state_stats[MCPS802154_PWR_STATE_RX].dur / + 1000000, + pwr_stats.power_state_stats[MCPS802154_PWR_STATE_RX].count); + if (rc) + goto nla_put_failure; + + /* Process the TX state. */ + rc = mcps802154_nl_put_pwr_stats_state( + msg, MCPS802154_PWR_STATS_ATTR_TX, + pwr_stats.power_state_stats[MCPS802154_PWR_STATE_TX].dur / + 1000000, + pwr_stats.power_state_stats[MCPS802154_PWR_STATE_TX].count); + if (rc) + goto nla_put_failure; + + /* Process the interrupts count. */ + if (nla_put_u32(msg, MCPS802154_PWR_STATS_ATTR_INTERRUPTS, + pwr_stats.interrupts)) { + rc = -EMSGSIZE; + goto nla_put_failure; + } + + nla_nest_end(msg, nl_pwr_stats); + genlmsg_end(msg, hdr); + return genlmsg_reply(msg, info); + +nla_put_failure: + genlmsg_cancel(msg, hdr); +failure: + nlmsg_free(msg); + return rc; +} + enum mcps802154_nl_internal_flags { MCPS802154_NL_NEED_HW = 1, }; @@ -1265,6 +1163,12 @@ static const struct genl_ops mcps802154_nl_ops[] = { .internal_flags = MCPS802154_NL_NEED_HW, }, { + .cmd = MCPS802154_CMD_CLOSE_SCHEDULER, + .doit = mcps802154_nl_close_scheduler, + .flags = GENL_ADMIN_PERM, + .internal_flags = MCPS802154_NL_NEED_HW, + }, + { .cmd = MCPS802154_CMD_SET_SCHEDULER_REGIONS, .doit = mcps802154_nl_generic_set_params, .flags = GENL_ADMIN_PERM, @@ -1291,12 +1195,6 @@ static const struct genl_ops mcps802154_nl_ops[] = { }, #endif { - .cmd = MCPS802154_CMD_SET_RANGING_REQUESTS, - .doit = mcps802154_nl_set_ranging_requests, - .flags = GENL_ADMIN_PERM, - .internal_flags = MCPS802154_NL_NEED_HW, - }, - { .cmd = MCPS802154_CMD_SET_CALIBRATIONS, .doit = mcps802154_nl_set_calibration, .flags = GENL_ADMIN_PERM, @@ -1314,6 +1212,12 @@ static const struct genl_ops mcps802154_nl_ops[] = { .flags = GENL_ADMIN_PERM, .internal_flags = MCPS802154_NL_NEED_HW, }, + { + .cmd = MCPS802154_CMD_GET_PWR_STATS, + .doit = mcps802154_nl_get_pwr_stats, + .flags = GENL_ADMIN_PERM, + .internal_flags = MCPS802154_NL_NEED_HW, + }, }; static struct genl_family mcps802154_nl_family __ro_after_init = { diff --git a/kernel/net/mcps802154/nl.h b/kernel/net/mcps802154/nl.h index eecab90..8bfe3bb 100644 --- a/kernel/net/mcps802154/nl.h +++ b/kernel/net/mcps802154/nl.h @@ -24,72 +24,6 @@ #ifndef MCPS802154_NL_H #define MCPS802154_NL_H -#include <linux/types.h> - -struct mcps802154_llhw; - -#define MCPS802154_NL_RANGING_REQUESTS_MAX 16 - -struct mcps802154_nl_ranging_request { - int id; - int frequency_hz; - __le64 peer_extended_addr; - __le64 remote_peer_extended_addr; -}; - -/** - * struct mcps802154_nl_ranging_report - Measures report. - */ -struct mcps802154_nl_ranging_report { - /** @tof_rctu: Time of Flight, or INT_MIN. */ - int tof_rctu; - /** @local_pdoa_rad_q11: Local Phase Difference Of Arrival, or INT_MIN. */ - int local_pdoa_rad_q11; - /** @remote_pdoa_rad_q11: Remote Phase Difference Of Arrival, or INT_MIN. */ - int remote_pdoa_rad_q11; - /** @local_pdoa_elevation_rad_q11: Local Phase Difference Of Arrival, or INT_MIN. */ - int local_pdoa_elevation_rad_q11; - /** @remote_pdoa_elevation_rad_q11: Remote Phase Difference Of Arrival, or INT_MIN. */ - int remote_pdoa_elevation_rad_q11; - /** @is_same_rx_ant_set_id: Has azimuth and elevation AoA been done with same antennas set? */ - bool is_same_rx_ant_set_id; -}; - -/** - * mcps802154_nl_ranging_report() - Report a ranging result, called from ranging - * code. - * @llhw: Low-level device pointer. - * @id: Ranging identifier. - * @report: Phase Difference Of Arrival and Time of Flight. - * - * If this returns -ECONNREFUSED, the receiver is not listening anymore, ranging - * can be stopped. - * - * Return: 0 or error. - */ -int mcps802154_nl_ranging_report( - struct mcps802154_llhw *llhw, int id, - const struct mcps802154_nl_ranging_report *report); - -#ifdef CONFIG_MCPS802154_TESTMODE -/** - * mcps802154_nl_ping_pong_report() - Report a ping pong result, called from - * factory tests code. - * @llhw: Low-level device pointer. - * @id: ping pong identifier. - * @t_0: t_0 of ping pong - * @t_3: t_3 of ping pong - * @t_4: t_4 of ping pong - * - * If this returns -ECONNREFUSED, the receiver is not listening anymore, - * ping pong can be stopped. - * - * Return: 0 or error. - */ -int mcps802154_nl_ping_pong_report(struct mcps802154_llhw *llhw, int id, - u64 t_0, u64 t_3, u64 t_4); -#endif - int mcps802154_nl_init(void); void mcps802154_nl_exit(void); diff --git a/kernel/net/mcps802154/ping_pong_region.c b/kernel/net/mcps802154/ping_pong_region.c deleted file mode 100644 index 42135d0..0000000 --- a/kernel/net/mcps802154/ping_pong_region.c +++ /dev/null @@ -1,581 +0,0 @@ -/* - * This file is part of the UWB stack for linux. - * - * Copyright (c) 2020-2021 Qorvo US, Inc. - * - * This software is provided under the GNU General Public License, version 2 - * (GPLv2), as well as under a Qorvo commercial license. - * - * You may choose to use this software under the terms of the GPLv2 License, - * version 2 ("GPLv2"), as published by the Free Software Foundation. - * You should have received a copy of the GPLv2 along with this program. If - * not, see <http://www.gnu.org/licenses/>. - * - * This program is distributed under the GPLv2 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 GPLv2 for more - * details. - * - * If you cannot meet the requirements of the GPLv2, you may not use this - * software for any purpose without first obtaining a commercial license from - * Qorvo. Please contact Qorvo to inquire about licensing terms. - */ -#include <asm/unaligned.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/ieee802154.h> -#include <net/af_ieee802154.h> -#include <net/mcps802154_schedule.h> -#include <net/mcps802154_frame.h> -#include <net/simple_ranging_region_nl.h> -#include "nl.h" -#include "warn_return.h" -#include "ping_pong_region.h" - -/* t_reply1 in RCTU - * t_reply1 is 400 microseconds, in RCTU: - * 400000000/15.6500400641026 = 25559039.99999994 - */ -#define TWR_FACTORY_TEST_T_REPLY1_RCTU (25559040ull * 2) - -/* t_reply2 in RCTU - * t_reply2 is 17000 microseconds, in RCTU: - * 17000000000/15.6500400641026 = 1086259199.999998 - */ -#define TWR_FACTORY_TEST_T_REPLY2_RCTU (1086259200ull) - -#define TWR_FACTORY_TEST_FUNCTION_CODE_POLL 0x0110 -#define TWR_FACTORY_TEST_FUNCTION_CODE_RESP 0x0111 -#define TWR_FACTORY_TEST_FUNCTION_CODE_FINAL 0x0112 - -#define PING_PONG_FRAME_SIZE 2 - -#define PING_PONG_FRAME_HEADER_SIZE \ - (IEEE802154_FC_LEN + IEEE802154_SEQ_LEN + IEEE802154_PAN_ID_LEN + \ - IEEE802154_SHORT_ADDR_LEN * 2) -#define PING_PONG_FRAME_MAX_SIZE \ - (PING_PONG_FRAME_HEADER_SIZE + PING_PONG_FRAME_SIZE) - -enum twr_factory_tests_frames { - TWR_FACTORY_TEST_FRAME_POLL, - TWR_FACTORY_TEST_FRAME_RESP, - TWR_FACTORY_TEST_FRAME_FINAL, - N_TWR_FACTORY_TEST_FRAMES, -}; - -struct ping_pong_initiator_time { - u64 t_0; - u64 t_3; - u64 t_4; -}; - -struct ping_pong_local { - struct mcps802154_scheduler scheduler; - struct mcps802154_llhw *llhw; - struct mcps802154_region region_init_active; - struct mcps802154_region region_init_idle; - struct mcps802154_region region_resp; - struct mcps802154_access access; - struct mcps802154_access_frame frames[N_TWR_FACTORY_TEST_FRAMES]; - struct mcps802154_nl_ranging_request ping_pong_request; - int region_init_active_duration_dtu; - bool enable_init_tx; - bool is_responder; - struct ping_pong_initiator_time initiator_time; -}; - -static inline struct ping_pong_local * -scheduler_to_ping_pong_local(const struct mcps802154_scheduler *scheduler) -{ - return container_of(scheduler, struct ping_pong_local, scheduler); -} - -static inline struct ping_pong_local * -region_init_active_to_ping_pong_local(struct mcps802154_region *region) -{ - return container_of(region, struct ping_pong_local, region_init_active); -} - -static inline struct ping_pong_local * -region_init_idle_to_ping_pong_local(struct mcps802154_region *region) -{ - return container_of(region, struct ping_pong_local, region_init_idle); -} - -static inline struct ping_pong_local * -region_resp_to_ping_pong_local(struct mcps802154_region *region) -{ - return container_of(region, struct ping_pong_local, region_resp); -} - -static inline struct ping_pong_local * -access_to_ping_pong_local(const struct mcps802154_access *access) -{ - return container_of(access, struct ping_pong_local, access); -} - -static void ping_pong_report(struct ping_pong_local *local, u64 t_0, u64 t_3, - u64 t_4) -{ - struct mcps802154_nl_ranging_request *request = - &local->ping_pong_request; - - mcps802154_nl_ping_pong_report(local->llhw, request->id, t_0, t_3, t_4); - /* Disable TX and force schedule update to change the region */ - local->enable_init_tx = false; - mcps802154_schedule_invalidate(local->llhw); -} - -void ping_pong_frame_header_fill_buf(u8 *buf, __le16 pan_id, __le16 dst, - __le16 src) -{ - u16 fc = (IEEE802154_FC_TYPE_DATA | IEEE802154_FC_INTRA_PAN | - (IEEE802154_ADDR_SHORT << IEEE802154_FC_DAMODE_SHIFT) | - (1 << IEEE802154_FC_VERSION_SHIFT) | - (IEEE802154_ADDR_SHORT << IEEE802154_FC_SAMODE_SHIFT)); - u8 seq = 0; - size_t pos = 0; - - put_unaligned_le16(fc, buf + pos); - pos += IEEE802154_FC_LEN; - buf[pos] = seq; - pos += IEEE802154_SEQ_LEN; - memcpy(buf + pos, &pan_id, sizeof(pan_id)); - pos += IEEE802154_PAN_ID_LEN; - memcpy(buf + pos, &dst, sizeof(dst)); - pos += IEEE802154_SHORT_ADDR_LEN; - memcpy(buf + pos, &src, sizeof(src)); -} - -void ping_pong_frame_header_put(struct sk_buff *skb, __le16 pan_id, __le16 dst, - __le16 src) -{ - ping_pong_frame_header_fill_buf( - skb_put(skb, PING_PONG_FRAME_HEADER_SIZE), pan_id, dst, src); -} - -static void ping_pong_resp_rx_frame(struct mcps802154_access *access, - int frame_idx, struct sk_buff *skb, - const struct mcps802154_rx_frame_info *info, - enum mcps802154_rx_error_type error) -{ - struct ping_pong_local *local = access_to_ping_pong_local(access); - struct mcps802154_nl_ranging_request *request = - &local->ping_pong_request; - u32 resp_tx_start_dtu; - - if (!skb) { - /* In case of NULL skb, to avoid the next TX, we adjust - * the frames count of region access. */ - access->n_frames = frame_idx + 1; - return; - } - request->peer_extended_addr = - get_unaligned_le64(skb->data + PING_PONG_FRAME_HEADER_SIZE - - IEEE802154_SHORT_ADDR_LEN); - resp_tx_start_dtu = - info->timestamp_dtu + - TWR_FACTORY_TEST_T_REPLY1_RCTU / local->llhw->dtu_rctu; - /* Set the timings for the next frames. */ - access->frames[TWR_FACTORY_TEST_FRAME_RESP].tx_frame_info.timestamp_dtu = - resp_tx_start_dtu; - access->frames[TWR_FACTORY_TEST_FRAME_FINAL].rx.info.timestamp_dtu = - resp_tx_start_dtu + - TWR_FACTORY_TEST_T_REPLY2_RCTU / local->llhw->dtu_rctu; - - kfree_skb(skb); - return; -} - -static struct sk_buff * -ping_pong_resp_tx_get_frame(struct mcps802154_access *access, int frame_idx) -{ - struct ping_pong_local *local = access_to_ping_pong_local(access); - struct mcps802154_nl_ranging_request *request = - &local->ping_pong_request; - struct sk_buff *skb = mcps802154_frame_alloc( - local->llhw, PING_PONG_FRAME_MAX_SIZE, GFP_KERNEL); - /* Extended address from mcps802154_nl_ranging_request are used as - * short address */ - ping_pong_frame_header_put(skb, mcps802154_get_pan_id(local->llhw), - request->peer_extended_addr, - mcps802154_get_short_addr(local->llhw)); - - if (WARN_ON(frame_idx != TWR_FACTORY_TEST_FRAME_RESP)) - return skb; - - skb_put_u8(skb, (u8)TWR_FACTORY_TEST_FUNCTION_CODE_RESP); - skb_put_u8(skb, (u8)(TWR_FACTORY_TEST_FUNCTION_CODE_RESP >> 8)); - return skb; -} - -static void ping_pong_tx_return(struct mcps802154_access *access, int frame_idx, - struct sk_buff *skb, - enum mcps802154_access_tx_return_reason reason) -{ - kfree_skb(skb); -} - -struct mcps802154_access_ops ping_pong_resp_access_ops = { - .rx_frame = ping_pong_resp_rx_frame, - .tx_get_frame = ping_pong_resp_tx_get_frame, - .tx_return = ping_pong_tx_return, -}; - -static struct mcps802154_access * -ping_pong_resp_get_access(struct mcps802154_region *region, - u32 next_timestamp_dtu, int next_in_region_dtu, - int region_duration_dtu) -{ - struct ping_pong_local *local = region_resp_to_ping_pong_local(region); - struct mcps802154_access *access = &local->access; - u32 start_dtu = next_timestamp_dtu; - - access->method = MCPS802154_ACCESS_METHOD_MULTI; - access->ops = &ping_pong_resp_access_ops; - access->n_frames = ARRAY_SIZE(local->frames); - access->frames = local->frames; - - access->frames[TWR_FACTORY_TEST_FRAME_POLL].is_tx = false; - access->frames[TWR_FACTORY_TEST_FRAME_POLL].rx.info.timestamp_dtu = - start_dtu; - access->frames[TWR_FACTORY_TEST_FRAME_POLL].rx.info.timeout_dtu = -1; - access->frames[TWR_FACTORY_TEST_FRAME_POLL].rx.info.flags = - MCPS802154_RX_INFO_TIMESTAMP_DTU | MCPS802154_RX_INFO_RANGING | - MCPS802154_RX_INFO_KEEP_RANGING_CLOCK; - access->frames[TWR_FACTORY_TEST_FRAME_POLL].rx.frame_info_flags_request = - MCPS802154_RX_FRAME_INFO_TIMESTAMP_DTU; - - access->frames[TWR_FACTORY_TEST_FRAME_RESP].is_tx = true; - access->frames[TWR_FACTORY_TEST_FRAME_RESP].tx_frame_info.flags = - MCPS802154_TX_FRAME_TIMESTAMP_DTU | - MCPS802154_TX_FRAME_RANGING | - MCPS802154_TX_FRAME_KEEP_RANGING_CLOCK; - access->frames[TWR_FACTORY_TEST_FRAME_RESP] - .tx_frame_info.rx_enable_after_tx_dtu = 0; - access->frames[TWR_FACTORY_TEST_FRAME_RESP] - .tx_frame_info.rx_enable_after_tx_timeout_dtu = 0; - - access->frames[TWR_FACTORY_TEST_FRAME_FINAL].is_tx = false; - access->frames[TWR_FACTORY_TEST_FRAME_FINAL].rx.info.flags = - MCPS802154_RX_INFO_TIMESTAMP_DTU | MCPS802154_RX_INFO_RANGING; - access->frames[TWR_FACTORY_TEST_FRAME_FINAL].rx.info.timeout_dtu = 0; - access->frames[TWR_FACTORY_TEST_FRAME_FINAL] - .rx.frame_info_flags_request = 0; - - return access; -} - -static struct mcps802154_region_ops ping_pong_region_resp_ops = { - .owner = THIS_MODULE, - .name = "ping-pong-resp", - .get_access = ping_pong_resp_get_access, -}; - -static void -ping_pong_init_idle_rx_frame(struct mcps802154_access *access, int frame_idx, - struct sk_buff *skb, - const struct mcps802154_rx_frame_info *info, - enum mcps802154_rx_error_type error) -{ - if (!skb) - return; - kfree_skb(skb); -} - -struct mcps802154_access_ops ping_pong_init_idle_access_ops = { - .rx_frame = ping_pong_init_idle_rx_frame, -}; - -static struct mcps802154_access * -ping_pong_init_idle_get_access(struct mcps802154_region *region, - u32 next_timestamp_dtu, int next_in_region_dtu, - int region_duration_dtu) -{ - struct ping_pong_local *local = - region_init_idle_to_ping_pong_local(region); - struct mcps802154_access *access = &local->access; - - access->method = MCPS802154_ACCESS_METHOD_IMMEDIATE_RX; - access->ops = &ping_pong_init_idle_access_ops; - return &local->access; -} - -static struct mcps802154_region_ops ping_pong_region_init_idle_ops = { - .owner = THIS_MODULE, - .name = "ping-pong-init-idle", - .get_access = ping_pong_init_idle_get_access, -}; - -static void -ping_pong_init_active_rx_frame(struct mcps802154_access *access, int frame_idx, - struct sk_buff *skb, - const struct mcps802154_rx_frame_info *info, - enum mcps802154_rx_error_type error) -{ - struct ping_pong_local *local = access_to_ping_pong_local(access); - u32 final_timestamp_dtu; - - if (!skb) { - /* Remote peer time out: set all time markers to 0 */ - local->initiator_time.t_0 = 0; - local->initiator_time.t_3 = 0; - local->initiator_time.t_4 = 0; - /* Avoid the next TX */ - access->n_frames = frame_idx + 1; - return; - } - final_timestamp_dtu = - info->timestamp_dtu + - TWR_FACTORY_TEST_T_REPLY2_RCTU / local->llhw->dtu_rctu; - local->initiator_time.t_3 = info->timestamp_rctu; - local->initiator_time.t_4 = mcps802154_tx_timestamp_dtu_to_rmarker_rctu( - local->llhw, final_timestamp_dtu, 0); - /* Set the timing for the final frame */ - access->frames[TWR_FACTORY_TEST_FRAME_FINAL] - .tx_frame_info.timestamp_dtu = final_timestamp_dtu; - - kfree_skb(skb); -} - -static struct sk_buff * -ping_pong_init_active_tx_get_frame(struct mcps802154_access *access, - int frame_idx) -{ - struct ping_pong_local *local = access_to_ping_pong_local(access); - struct mcps802154_nl_ranging_request *request = - &local->ping_pong_request; - struct sk_buff *skb = mcps802154_frame_alloc( - local->llhw, PING_PONG_FRAME_MAX_SIZE, GFP_KERNEL); - - ping_pong_frame_header_put(skb, mcps802154_get_pan_id(local->llhw), - request->peer_extended_addr, - mcps802154_get_short_addr(local->llhw)); - if (frame_idx == TWR_FACTORY_TEST_FRAME_POLL) { - skb_put_u8(skb, (u8)TWR_FACTORY_TEST_FUNCTION_CODE_POLL); - skb_put_u8(skb, (u8)(TWR_FACTORY_TEST_FUNCTION_CODE_POLL >> 8)); - } else { - WARN_ON(frame_idx != TWR_FACTORY_TEST_FRAME_FINAL); - skb_put_u8(skb, (u8)TWR_FACTORY_TEST_FUNCTION_CODE_FINAL); - skb_put_u8(skb, - (u8)(TWR_FACTORY_TEST_FUNCTION_CODE_FINAL >> 8)); - } - return skb; -} - -static void ping_pong_init_active_access_done(struct mcps802154_access *access, - bool error) -{ - struct ping_pong_local *local = access_to_ping_pong_local(access); - - ping_pong_report(local, local->initiator_time.t_0, - local->initiator_time.t_3, local->initiator_time.t_4); -} - -struct mcps802154_access_ops ping_pong_init_active_access_ops = { - .common = { - .access_done = ping_pong_init_active_access_done, - }, - .rx_frame = ping_pong_init_active_rx_frame, - .tx_get_frame = ping_pong_init_active_tx_get_frame, - .tx_return = ping_pong_tx_return, -}; - -static struct mcps802154_access * -ping_pong_init_active_get_access(struct mcps802154_region *region, - u32 next_timestamp_dtu, int next_in_region_dtu, - int region_duration_dtu) -{ - struct ping_pong_local *local = - region_init_active_to_ping_pong_local(region); - struct mcps802154_access *access = &local->access; - u32 start_dtu; - - /* Only send frames on time per schedule */ - if (next_in_region_dtu != 0) { - return NULL; - } - start_dtu = next_timestamp_dtu; - access->method = MCPS802154_ACCESS_METHOD_MULTI; - access->n_frames = ARRAY_SIZE(local->frames); - access->frames = local->frames; - /* Hard-coded! */ - access->n_frames = ARRAY_SIZE(local->frames); - access->frames[TWR_FACTORY_TEST_FRAME_POLL].is_tx = true; - access->frames[TWR_FACTORY_TEST_FRAME_POLL].tx_frame_info.timestamp_dtu = - start_dtu; - access->frames[TWR_FACTORY_TEST_FRAME_POLL].tx_frame_info.flags = - MCPS802154_TX_FRAME_TIMESTAMP_DTU | - MCPS802154_TX_FRAME_RANGING | - MCPS802154_TX_FRAME_KEEP_RANGING_CLOCK; - access->frames[TWR_FACTORY_TEST_FRAME_POLL] - .tx_frame_info.rx_enable_after_tx_dtu = 0; - access->frames[TWR_FACTORY_TEST_FRAME_POLL] - .tx_frame_info.rx_enable_after_tx_timeout_dtu = 0; - local->initiator_time.t_0 = mcps802154_tx_timestamp_dtu_to_rmarker_rctu( - local->llhw, start_dtu, 0); - - access->frames[TWR_FACTORY_TEST_FRAME_RESP].is_tx = false; - access->frames[TWR_FACTORY_TEST_FRAME_RESP].rx.info.timestamp_dtu = - start_dtu + - TWR_FACTORY_TEST_T_REPLY1_RCTU / local->llhw->dtu_rctu; - access->frames[TWR_FACTORY_TEST_FRAME_RESP].rx.info.timeout_dtu = 0; - access->frames[TWR_FACTORY_TEST_FRAME_RESP].rx.info.flags = - MCPS802154_RX_INFO_TIMESTAMP_DTU | MCPS802154_RX_INFO_RANGING | - MCPS802154_RX_INFO_KEEP_RANGING_CLOCK; - access->frames[TWR_FACTORY_TEST_FRAME_RESP].rx.frame_info_flags_request = - MCPS802154_RX_FRAME_INFO_TIMESTAMP_DTU | - MCPS802154_RX_FRAME_INFO_TIMESTAMP_RCTU; - - access->frames[TWR_FACTORY_TEST_FRAME_FINAL].is_tx = true; - access->frames[TWR_FACTORY_TEST_FRAME_FINAL] - .tx_frame_info.timestamp_dtu = - start_dtu + (TWR_FACTORY_TEST_T_REPLY1_RCTU + - TWR_FACTORY_TEST_T_REPLY2_RCTU) / - local->llhw->dtu_rctu; - access->frames[TWR_FACTORY_TEST_FRAME_FINAL].tx_frame_info.flags = - MCPS802154_TX_FRAME_TIMESTAMP_DTU | MCPS802154_TX_FRAME_RANGING; - access->frames[TWR_FACTORY_TEST_FRAME_FINAL] - .tx_frame_info.rx_enable_after_tx_dtu = 0; - access->frames[TWR_FACTORY_TEST_FRAME_FINAL] - .tx_frame_info.rx_enable_after_tx_timeout_dtu = 0; - access->ops = &ping_pong_init_active_access_ops; - return &local->access; -} - -static struct mcps802154_region_ops ping_pong_region_init_active_ops = { - .owner = THIS_MODULE, - .name = "ping-pong-init-active", - .get_access = ping_pong_init_active_get_access, -}; - -static struct mcps802154_scheduler * -ping_pong_scheduler_open(struct mcps802154_llhw *llhw) -{ - struct ping_pong_local *local; - - local = kzalloc(sizeof(*local), GFP_KERNEL); - if (!local) - return NULL; - local->llhw = llhw; - local->region_init_active.ops = &ping_pong_region_init_active_ops; - local->region_init_idle.ops = &ping_pong_region_init_idle_ops; - local->region_resp.ops = &ping_pong_region_resp_ops; - local->is_responder = false; - local->enable_init_tx = false; - local->region_init_active_duration_dtu = - (TWR_FACTORY_TEST_T_REPLY1_RCTU + - TWR_FACTORY_TEST_T_REPLY2_RCTU) / - local->llhw->dtu_rctu; - return &local->scheduler; -} - -static void ping_pong_scheduler_close(struct mcps802154_scheduler *scheduler) -{ - struct ping_pong_local *ping_pong_local = - scheduler_to_ping_pong_local(scheduler); - kfree(ping_pong_local); -} - -static int ping_pong_scheduler_update_schedule( - struct mcps802154_scheduler *scheduler, - const struct mcps802154_schedule_update *schedule_update, - u32 next_timestamp_dtu) -{ - struct ping_pong_local *local = scheduler_to_ping_pong_local(scheduler); - int r; - - /* Remove the region in the schedule */ - r = mcps802154_schedule_recycle(schedule_update, 0, - MCPS802154_DURATION_NO_CHANGE); - WARN_RETURN(r); - - if (local->is_responder) { - r = mcps802154_schedule_add_region(schedule_update, - &local->region_resp, 0, 0); - } else { - if (local->enable_init_tx) { - r = mcps802154_schedule_add_region( - schedule_update, &local->region_init_active, 0, - local->region_init_active_duration_dtu); - } else { - r = mcps802154_schedule_add_region( - schedule_update, &local->region_init_idle, 0, - 0); - } - } - return r; -} - -static int ping_pong_scheduler_ping_pong_setup( - struct mcps802154_scheduler *scheduler, - const struct mcps802154_nl_ranging_request *requests, - unsigned int n_requests) -{ - struct ping_pong_local *local = scheduler_to_ping_pong_local(scheduler); - - if (local->is_responder) - return -EOPNOTSUPP; - if (n_requests != 1) - return -EINVAL; - if (requests[0].remote_peer_extended_addr) - return -EOPNOTSUPP; - local->ping_pong_request = requests[0]; - /* Enable TX and force schedule update to change the region */ - local->enable_init_tx = true; - mcps802154_schedule_invalidate(local->llhw); - return 0; -} - -static int -ping_pong_scheduler_set_parameters(struct mcps802154_scheduler *scheduler, - const struct nlattr *params_attr, - struct netlink_ext_ack *extack) -{ - struct ping_pong_local *local = scheduler_to_ping_pong_local(scheduler); - struct nlattr *attrs[SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_MAX + 1]; - int r; - static const struct nla_policy nla_policy[SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_MAX + - 1] = { - [SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_NODE_TYPE] = { .type = NLA_U32 }, - }; - - r = nla_parse_nested(attrs, - SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_MAX, - params_attr, nla_policy, extack); - if (r) - return r; - - if (attrs[SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_NODE_TYPE]) { - u32 type = nla_get_u32( - attrs[SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_NODE_TYPE]); - - if (type > 1) - return -EINVAL; - - local->is_responder = type == 1 ? true : false; - mcps802154_schedule_invalidate(local->llhw); - } - - return 0; -} - -static struct mcps802154_scheduler_ops ping_pong_region_scheduler = { - .owner = THIS_MODULE, - .name = "ping-pong", - .open = ping_pong_scheduler_open, - .close = ping_pong_scheduler_close, - .update_schedule = ping_pong_scheduler_update_schedule, - .ranging_setup = ping_pong_scheduler_ping_pong_setup, - .set_parameters = ping_pong_scheduler_set_parameters, -}; - -int __init ping_pong_region_init(void) -{ - return mcps802154_scheduler_register(&ping_pong_region_scheduler); -} - -void __exit ping_pong_region_exit(void) -{ - mcps802154_scheduler_unregister(&ping_pong_region_scheduler); -} diff --git a/kernel/net/mcps802154/simple_ranging_region.c b/kernel/net/mcps802154/simple_ranging_region.c deleted file mode 120000 index 87111a1..0000000 --- a/kernel/net/mcps802154/simple_ranging_region.c +++ /dev/null @@ -1 +0,0 @@ -../../../mac/simple_ranging_region.c
\ No newline at end of file diff --git a/kernel/net/mcps802154/simple_ranging_region.h b/kernel/net/mcps802154/simple_ranging_region.h deleted file mode 120000 index f8e6e18..0000000 --- a/kernel/net/mcps802154/simple_ranging_region.h +++ /dev/null @@ -1 +0,0 @@ -../../../mac/simple_ranging_region.h
\ No newline at end of file @@ -29,7 +29,7 @@ #include "schedulers.h" #include "trace.h" -struct mcps802154_access_common_ops idle_access_ops = {}; +struct mcps802154_access_common_ops ca_access_ops = {}; static int mcps802154_ca_trace_int(struct mcps802154_local *local, const int r) { @@ -109,7 +109,7 @@ int mcps802154_ca_start(struct mcps802154_local *local) } local->start_stop_request = true; - local->fproc.state->schedule_change(local); + mcps802154_fproc_schedule_change(local); return local->started ? 0 : -EIO; } @@ -117,7 +117,7 @@ int mcps802154_ca_start(struct mcps802154_local *local) void mcps802154_ca_stop(struct mcps802154_local *local) { local->start_stop_request = false; - local->fproc.state->schedule_change(local); + mcps802154_fproc_schedule_change(local); } void mcps802154_ca_notify_stop(struct mcps802154_local *local) @@ -332,18 +332,28 @@ static int mcps802154_ca_next_region(struct mcps802154_local *local, struct mcps802154_schedule_region *sched_region; int next_dtu = next_timestamp_dtu - sched->start_timestamp_dtu; bool changed = 0; + bool once; sched_region = &sched->regions[sched->current_index]; + once = sched_region->once; - /* If not an endless region, need to test if still inside. */ - while (sched_region->duration_dtu != 0 && - next_dtu - sched_region->start_dtu >= - sched_region->duration_dtu) { + /* If the region schedule is over, select the next region if + * possible. */ + while (once || (sched_region->duration_dtu != 0 && + next_dtu - sched_region->start_dtu >= + sched_region->duration_dtu)) { sched->current_index++; changed = 1; + once = false; /* No more region, need a new schedule. */ if (sched->current_index >= sched->n_regions) { + /* Reduce the schedule duration when not fully used. */ + if (sched_region->once && sched_region->duration_dtu) { + sched->duration_dtu = + next_timestamp_dtu - + sched->start_timestamp_dtu; + } return mcps802154_schedule_update(local, next_timestamp_dtu); } @@ -355,21 +365,21 @@ static int mcps802154_ca_next_region(struct mcps802154_local *local, } /** - * mcps802154_ca_idle() - Fill and return an idle access. + * mcps802154_ca_nothing() - Fill and return a nothing access. * @local: MCPS private data. - * @timestamp_dtu: Start of idle period. - * @duration_dtu: Duration of idle period, or 0 for endless. + * @timestamp_dtu: Start of nothing period. + * @duration_dtu: Duration of nothing period, or 0 for endless. * * Return: Pointer to access allocated inside the context. */ -struct mcps802154_access *mcps802154_ca_idle(struct mcps802154_local *local, - u32 timestamp_dtu, - int duration_dtu) +struct mcps802154_access *mcps802154_ca_nothing(struct mcps802154_local *local, + u32 timestamp_dtu, + int duration_dtu) { struct mcps802154_access *access = &local->ca.idle_access; access->method = MCPS802154_ACCESS_METHOD_NOTHING; - access->common_ops = &idle_access_ops; + access->common_ops = &ca_access_ops; access->timestamp_dtu = timestamp_dtu; access->duration_dtu = duration_dtu; return access; @@ -409,7 +419,8 @@ mcps802154_ca_get_access(struct mcps802154_local *local, u32 next_timestamp_dtu) /* Stay in IDLE when no schedule. */ if (r == -ENOENT) - return mcps802154_ca_idle(local, false, 0); + return mcps802154_ca_nothing(local, next_timestamp_dtu, + 0); else if (r < 0) return NULL; @@ -420,11 +431,12 @@ mcps802154_ca_get_access(struct mcps802154_local *local, u32 next_timestamp_dtu) sched->start_timestamp_dtu + sched_region->start_dtu; region_duration_dtu = sched_region->duration_dtu; - /* If the region has changed, access date may be postponed. */ if (changed) { if (is_before_dtu(next_timestamp_dtu, - region_start_timestamp_dtu)) + region_start_timestamp_dtu)) { + /* Access date may be postponed. */ next_timestamp_dtu = region_start_timestamp_dtu; + } } /* Get access. */ @@ -439,6 +451,7 @@ mcps802154_ca_get_access(struct mcps802154_local *local, u32 next_timestamp_dtu) access = region->ops->get_access(region, next_timestamp_dtu, next_in_region_dtu, region_duration_dtu); + if (access) return access; @@ -450,7 +463,7 @@ mcps802154_ca_get_access(struct mcps802154_local *local, u32 next_timestamp_dtu) if (is_before_dtu(idle_timestamp_dtu, region_end_timestamp_dtu)) { - return mcps802154_ca_idle( + return mcps802154_ca_nothing( local, next_timestamp_dtu, region_end_timestamp_dtu - next_timestamp_dtu); @@ -459,7 +472,8 @@ mcps802154_ca_get_access(struct mcps802154_local *local, u32 next_timestamp_dtu) /* Continue after the current region. */ next_timestamp_dtu = region_end_timestamp_dtu; } else { - return mcps802154_ca_idle(local, next_timestamp_dtu, 0); + return mcps802154_ca_nothing(local, next_timestamp_dtu, + 0); } } } @@ -467,7 +481,7 @@ mcps802154_ca_get_access(struct mcps802154_local *local, u32 next_timestamp_dtu) void mcps802154_ca_may_reschedule(struct mcps802154_local *local) { if (!local->ca.held) - local->fproc.state->schedule_change(local); + mcps802154_fproc_schedule_change(local); } void mcps802154_ca_access_hold(struct mcps802154_local *local) @@ -479,5 +493,5 @@ void mcps802154_ca_invalidate_schedule(struct mcps802154_local *local) { local->ca.reset = true; - local->fproc.state->schedule_change(local); + mcps802154_fproc_schedule_change(local); } diff --git a/mac/endless_scheduler.c b/mac/endless_scheduler.c index 32fc958..76ee7f8 100644 --- a/mac/endless_scheduler.c +++ b/mac/endless_scheduler.c @@ -92,7 +92,8 @@ static int mcps802154_endless_scheduler_update_schedule( /* Can not fail, only possible error is invalid parameters. */ WARN_RETURN(r); - r = mcps802154_schedule_add_region(schedule_update, region, 0, 0); + r = mcps802154_schedule_add_region(schedule_update, region, 0, 0, + false); return r; } diff --git a/mac/fira_access.c b/mac/fira_access.c index 9146f78..0cddc41 100644 --- a/mac/fira_access.c +++ b/mac/fira_access.c @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2020-2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. @@ -21,6 +21,7 @@ * Qorvo. Please contact Qorvo to inquire about licensing terms. */ +#include "fira_round_hopping_sequence.h" #include "fira_access.h" #include "fira_session.h" #include "fira_frame.h" @@ -31,14 +32,13 @@ #include <linux/ieee802154.h> #include <linux/math64.h> #include <linux/limits.h> +#include <linux/errno.h> #include <net/mcps802154_frame.h> #include "warn_return.h" #define FIRA_STS_FOM_THRESHOLD 153 - -static struct mcps802154_access * -fira_access_controlee(struct fira_local *local, struct fira_session *session); +#define FIRA_RSSI_MAX 0xff /** * sat_fp() - Saturate the range of fixed-point @@ -90,16 +90,17 @@ static void fira_access_setup_frame(struct fira_local *local, bool is_tx) { const struct fira_session_params *params = &session->params; + struct mcps802154_access *access = &local->access; struct mcps802154_sts_params *sts_params_for_access = NULL; int rframe_config = session->params.rframe_config; const struct fira_measurement_sequence_step *current_ms_step = - fira_session_get_current_meas_seq_step(session); + fira_session_get_meas_seq_step(session); bool is_rframe = slot->message_id <= FIRA_MESSAGE_ID_RFRAME_MAX; bool is_last_rframe = slot->message_id == FIRA_MESSAGE_ID_RANGING_FINAL; bool is_first_frame = slot->message_id == FIRA_MESSAGE_ID_CONTROL; - + bool request_rssi = session->params.report_rssi; if (is_rframe) { memcpy(sts_params->v, session->crypto.sts_v, AES_BLOCK_SIZE); put_unaligned_be32(slot->index, @@ -108,8 +109,12 @@ static void fira_access_setup_frame(struct fira_local *local, session->crypto.derived_authentication_key, AES_KEYSIZE_128); /* Constant for the moment. */ - sts_params->n_segs = 1; - sts_params->seg_len = 64; + sts_params->n_segs = params->number_of_sts_segments; + sts_params->seg_len = + params->sts_length == FIRA_STS_LENGTH_128 ? + 128 : + params->sts_length == FIRA_STS_LENGTH_32 ? 32 : + 64; sts_params->sp2_tx_gap_4chips = 0; sts_params->sp2_rx_gap_4chips[0] = 0; sts_params->sp2_rx_gap_4chips[1] = 0; @@ -119,7 +124,7 @@ static void fira_access_setup_frame(struct fira_local *local, } if (is_tx) { - u8 flags = MCPS802154_TX_FRAME_TIMESTAMP_DTU; + u8 flags = MCPS802154_TX_FRAME_CONFIG_TIMESTAMP_DTU; /* Add a small margin to the Tx timestamps. */ if (!is_first_frame) @@ -134,21 +139,23 @@ static void fira_access_setup_frame(struct fira_local *local, ranging_info->timestamps_rctu[slot->message_id] = mcps802154_tx_timestamp_dtu_to_rmarker_rctu( local->llhw, frame_dtu, + access->hrp_uwb_params, access->channel, slot->tx_ant_set); - flags |= MCPS802154_TX_FRAME_RANGING; + flags |= MCPS802154_TX_FRAME_CONFIG_RANGING; if (rframe_config == FIRA_RFRAME_CONFIG_SP3) - flags |= MCPS802154_TX_FRAME_SP3; + flags |= MCPS802154_TX_FRAME_CONFIG_SP3; else - flags |= MCPS802154_TX_FRAME_SP1; + flags |= MCPS802154_TX_FRAME_CONFIG_SP1; if (!is_last_rframe) - flags |= MCPS802154_TX_FRAME_KEEP_RANGING_CLOCK; + flags |= + MCPS802154_TX_FRAME_CONFIG_KEEP_RANGING_CLOCK; } else if (is_first_frame) { - flags |= MCPS802154_TX_FRAME_RANGING_ROUND; + flags |= MCPS802154_TX_FRAME_CONFIG_RANGING_ROUND; } *frame = (struct mcps802154_access_frame){ .is_tx = true, - .tx_frame_info = { + .tx_frame_config = { .timestamp_dtu = frame_dtu, .flags = flags, .ant_set_id = slot->tx_ant_set, @@ -156,22 +163,26 @@ static void fira_access_setup_frame(struct fira_local *local, .sts_params = sts_params_for_access, }; } else { - u8 flags = MCPS802154_RX_INFO_TIMESTAMP_DTU; + u8 flags = MCPS802154_RX_FRAME_CONFIG_TIMESTAMP_DTU; u16 request = 0; + if (request_rssi) + request |= MCPS802154_RX_FRAME_INFO_RSSI; if (is_rframe) { - flags |= MCPS802154_RX_INFO_RANGING; + flags |= MCPS802154_RX_FRAME_CONFIG_RANGING; if (rframe_config == FIRA_RFRAME_CONFIG_SP3) - flags |= MCPS802154_RX_INFO_SP3; + flags |= MCPS802154_RX_FRAME_CONFIG_SP3; else - flags |= MCPS802154_RX_INFO_SP1; + flags |= MCPS802154_RX_FRAME_CONFIG_SP1; if (!is_last_rframe) - flags |= MCPS802154_RX_INFO_KEEP_RANGING_CLOCK; - request = MCPS802154_RX_FRAME_INFO_TIMESTAMP_RCTU | - MCPS802154_RX_FRAME_INFO_RANGING_STS_FOM; + flags |= + MCPS802154_RX_FRAME_CONFIG_KEEP_RANGING_CLOCK; + request |= MCPS802154_RX_FRAME_INFO_TIMESTAMP_RCTU | + MCPS802154_RX_FRAME_INFO_RANGING_STS_FOM; if (current_ms_step->type != FIRA_MEASUREMENT_TYPE_RANGE) { - flags |= MCPS802154_RX_INFO_RANGING_PDOA; + flags |= + MCPS802154_RX_FRAME_CONFIG_RANGING_PDOA; request |= MCPS802154_RX_FRAME_INFO_RANGING_PDOA | MCPS802154_RX_FRAME_INFO_RANGING_PDOA_FOM; @@ -186,7 +197,7 @@ static void fira_access_setup_frame(struct fira_local *local, *frame = (struct mcps802154_access_frame){ .is_tx = false, .rx = { - .info = { + .frame_config = { .timestamp_dtu = frame_dtu, .flags = flags, .ant_set_id = slot->rx_ant_set, @@ -198,14 +209,40 @@ static void fira_access_setup_frame(struct fira_local *local, } } +static void fira_controlee_resync(struct fira_session *session, u32 sts_index, + u32 timestamp_dtu) +{ + const struct fira_session_params *params = &session->params; + /* Variable for session resync. */ + int slots_per_block = + params->block_duration_dtu / params->slot_duration_dtu; + int sts_offset = sts_index - session->crypto.sts_index_init; + int block_index = sts_offset / slots_per_block; + int slot_index = sts_offset - block_index * slots_per_block; + int round_index = slot_index / params->round_duration_slots; + int block_start_dtu = + timestamp_dtu - slot_index * params->slot_duration_dtu; + + /* Update the session. */ + session->block_start_dtu = block_start_dtu; + session->block_index = block_index; + session->sts_index = sts_index - slot_index; + session->round_index = round_index; + session->controlee.synchronised = true; + session->controlee.next_round_index_valid = false; + session->controlee.block_index_sync = block_index; +} + static bool fira_rx_sts_good(struct fira_local *local, const struct mcps802154_rx_frame_info *info) { + int i; if (!(info->flags & MCPS802154_RX_FRAME_INFO_RANGING_STS_FOM)) return false; - /* Only one segment for the moment. */ - if (info->ranging_sts_fom[0] < FIRA_STS_FOM_THRESHOLD) - return false; + for (i = 0; i < MCPS802154_STS_N_SEGS_MAX; i++) { + if (info->ranging_sts_fom[i] < FIRA_STS_FOM_THRESHOLD) + return false; + } return true; } @@ -220,16 +257,166 @@ static void fira_ranging_info_set_status(struct fira_ranging_info *ranging_info, ranging_info->slot_index = slot_index; } +static void +fira_diagnostic_rssis(const struct mcps802154_rx_measurement_info *info, + struct fira_diagnostic *diagnostic) +{ + if (info->flags & MCPS802154_RX_MEASUREMENTS_RSSIS) { + int max = max(MCPS802154_RSSIS_N_MAX - 1, info->n_rssis); + int i; + for (i = 0; i < max; i++) + diagnostic->rssis_q1[i] = info->rssis_q1[i]; + diagnostic->n_rssis = i; + } +} + +static void +fira_diagnostic_aoas(const struct mcps802154_rx_measurement_info *info, + struct fira_diagnostic *diagnostic) +{ + int max = max(MCPS802154_RX_AOA_MEASUREMENTS_MAX - 1, info->n_aoas); + int i; + + for (i = 0; i < max; i++) + diagnostic->aoas[i] = info->aoas[i]; + diagnostic->n_aoas = info->n_aoas; +} + +static struct mcps802154_rx_cir * +fira_diagnostic_cirs_alloc(const struct mcps802154_rx_measurement_info *info) +{ + const struct mcps802154_rx_cir_sample_window *si; + struct mcps802154_rx_cir_sample_window *so; + struct mcps802154_rx_cir *cirs; + int i; + int j; + + cirs = kmalloc(info->n_cirs * sizeof(struct mcps802154_rx_cir), + GFP_KERNEL); + if (!cirs) + return NULL; + + for (i = 0; i < info->n_cirs; i++) { + so = &cirs[i].sample_window; + si = &info->cirs[i].sample_window; + so->samples = + kmalloc(si->n_samples * si->sizeof_sample, GFP_KERNEL); + + if (!so->samples) + goto failed; + } + return cirs; + +failed: + for (j = 0; j < i; j++) + kfree(cirs[j].sample_window.samples); + kfree(cirs); + return NULL; +} + +static void +fira_diagnostic_cirs_copy(const struct mcps802154_rx_measurement_info *info, + struct fira_diagnostic *diagnostic) +{ + int i; + + for (i = 0; i < info->n_cirs; i++) { + struct mcps802154_rx_cir *cir_in; + struct mcps802154_rx_cir *cir_out; + struct mcps802154_rx_cir_sample_window *si; + struct mcps802154_rx_cir_sample_window *so; + + cir_out = &diagnostic->cirs[i]; + cir_in = &info->cirs[i]; + so = &cir_out->sample_window; + si = &cir_in->sample_window; + + cir_out->fp_index = cir_in->fp_index; + cir_out->fp_snr = cir_in->fp_snr; + cir_out->fp_ns_q6 = cir_in->fp_ns_q6; + cir_out->pp_index = cir_in->pp_index; + cir_out->pp_snr = cir_in->pp_snr; + cir_out->pp_ns_q6 = cir_in->pp_ns_q6; + cir_out->fp_sample_offset = cir_in->fp_sample_offset; + so->n_samples = si->n_samples; + so->sizeof_sample = si->sizeof_sample; + + memcpy(so->samples, si->samples, + si->n_samples * si->sizeof_sample); + } + diagnostic->n_cirs = i; +} + +static void +fira_diagnostic_cirs(const struct mcps802154_rx_measurement_info *info, + struct fira_diagnostic *diagnostic) +{ + if (info->flags & MCPS802154_RX_MEASUREMENTS_CIRS) { + diagnostic->cirs = fira_diagnostic_cirs_alloc(info); + if (diagnostic->cirs) + fira_diagnostic_cirs_copy(info, diagnostic); + else + diagnostic->n_cirs = 0; + } +} + +static void fira_diagnostic(struct fira_local *local, + struct fira_session *session, void *rx_ctx, + int slot_idx) +{ + const struct fira_session_params *params = &session->params; + struct fira_diagnostic *diagnostic = &local->diagnostics[slot_idx]; + struct mcps802154_rx_measurement_info info = {}; + int r; + + WARN_ON(diagnostic->cirs); + + if (params->diagnostic_report_flags & + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORT_RSSIS) + info.flags |= MCPS802154_RX_MEASUREMENTS_RSSIS; + if (params->diagnostic_report_flags & + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORT_CIRS) + info.flags |= MCPS802154_RX_MEASUREMENTS_CIRS; + + if (!info.flags) + return; + + r = mcps802154_rx_get_measurement(local->llhw, rx_ctx, &info); + + if (r) + return; + + fira_diagnostic_rssis(&info, diagnostic); + fira_diagnostic_cirs(&info, diagnostic); +} + +static void fira_diagnostic_free(struct fira_local *local) +{ + int i; + + for (i = 0; i < local->access.n_frames; i++) { + struct fira_diagnostic *diagnostic = &local->diagnostics[i]; + int j; + + for (j = 0; j < diagnostic->n_cirs; j++) + kfree(diagnostic->cirs[j].sample_window.samples); + + kfree(diagnostic->cirs); + diagnostic->cirs = NULL; + diagnostic->n_cirs = 0; + } +} + static void fira_rx_frame_ranging(struct fira_local *local, const struct fira_slot *slot, struct sk_buff *skb, const struct mcps802154_rx_frame_info *info) { const struct fira_session *session = local->current_session; + const struct fira_session_params *params = &session->params; struct fira_ranging_info *ranging_info = &local->ranging_info[slot->ranging_index]; struct mcps802154_ie_get_context ie_get = {}; - bool pdoa_info_present; if (!fira_rx_sts_good(local, info)) { @@ -253,10 +440,29 @@ static void fira_rx_frame_ranging(struct fira_local *local, struct fira_local_aoa_info *local_aoa; bool pdoa_fom_info_present = info->flags & MCPS802154_RX_FRAME_INFO_RANGING_PDOA_FOM; - s16 local_pdoa_q11 = info->ranging_pdoa_rad_q11; - s16 local_aoa_q11 = info->ranging_aoa_rad_q11; + s16 local_pdoa_q11 = 0; + s16 local_aoa_q11 = 0; const struct fira_measurement_sequence_step *current_step = - fira_session_get_current_meas_seq_step(session); + fira_session_get_meas_seq_step(session); + struct mcps802154_rx_measurement_info meas_info = {}; + int r; + + meas_info.flags |= MCPS802154_RX_MEASUREMENTS_AOAS; + r = mcps802154_rx_get_measurement( + local->llhw, ranging_info->rx_ctx, &meas_info); + + if (!r && meas_info.flags & MCPS802154_RX_MEASUREMENTS_AOAS && + meas_info.n_aoas) { + struct fira_diagnostic *diagnostic = + &local->diagnostics[slot->index]; + + /* TODO: Find which aoas index to use. */ + local_pdoa_q11 = meas_info.aoas[0].pdoa_rad_q11; + local_aoa_q11 = meas_info.aoas[0].aoa_rad_q11; + if (params->diagnostic_report_flags & + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORT_AOAS) + fira_diagnostic_aoas(&meas_info, diagnostic); + } switch (current_step->type) { case FIRA_MEASUREMENT_TYPE_AOA: @@ -287,7 +493,7 @@ static void fira_rx_frame_ranging(struct fira_local *local, local_aoa->pdoa_2pi = map_q11_to_2pi(local_pdoa_q11); local_aoa->aoa_2pi = map_q11_to_2pi(local_aoa_q11); /* LCOV_EXCL_START */ - /* FoM is always expected when PDoA present */ + /* FoM is always expected when PDoA present. */ if (pdoa_fom_info_present) /* LCOV_EXCL_STOP */ local_aoa->aoa_fom = info->ranging_pdoa_fom; @@ -302,8 +508,8 @@ static void fira_rx_frame_ranging(struct fira_local *local, } if (skb) { - if (fira_frame_header_check_decrypt(local, slot, skb, &ie_get, - NULL, NULL) || + if (fira_frame_header_check_decrypt(local, slot, skb, + &ie_get) || !fira_frame_rframe_payload_check(local, slot, skb, &ie_get)) { fira_ranging_info_set_status( @@ -319,75 +525,94 @@ static void fira_rx_frame_control(struct fira_local *local, struct sk_buff *skb, const struct mcps802154_rx_frame_info *info) { - struct fira_ranging_info *ranging_info = + struct mcps802154_access *access = &local->access; + struct fira_ranging_info *ri = &local->ranging_info[slot->ranging_index]; - struct fira_session *session = local->current_session; - struct fira_session *allow_resync_session = NULL; struct mcps802154_ie_get_context ie_get = {}; + const struct fira_session_params *params = NULL; + struct fira_session *session; + int last_slot_index = 0; + int offset_in_access_duration_dtu; + int left_duration_dtu; + unsigned n_slots; u32 sts_index; - unsigned int n_slots; - int last_slot_index, block_stride_len; - int i; - bool stop_ranging; + u8 *header; + int r; if (!(info->flags & MCPS802154_RX_FRAME_INFO_TIMESTAMP_DTU)) { fira_ranging_info_set_status( - ranging_info, FIRA_STATUS_RANGING_RX_PHY_DEC_FAILED, - slot->index); + ri, FIRA_STATUS_RANGING_RX_PHY_DEC_FAILED, slot->index); return; } - if (fira_frame_header_check_decrypt( - local, slot, skb, &ie_get, &sts_index, - session->synchronised ? NULL : &allow_resync_session)) - goto failed; - if (allow_resync_session) { - session = local->current_session = allow_resync_session; - fira_session_prepare(session); - fira_access_controlee(local, session); - } - if (!fira_frame_control_payload_check(local, skb, &ie_get, &n_slots, - &stop_ranging, &block_stride_len)) + offset_in_access_duration_dtu = + info->timestamp_dtu - access->timestamp_dtu; + + /* Read the header to capture the session context. */ + header = skb->data; + session = fira_rx_frame_control_header_check(local, slot, skb, &ie_get, + &sts_index); + if (!session) goto failed; + params = &session->params; + ri->rx_ctx = session->rx_ctx[0]; - fira_session_resync(session, sts_index, info->timestamp_dtu); + /* Continue to decode the frame. */ + r = fira_frame_decrypt(local, session, slot, skb, skb->data - header); + if (r) + goto failed; + r = fira_frame_control_payload_check(local, skb, &ie_get, &n_slots, + &session->stop_inband, + &session->block_stride_len); + if (!r) + goto failed; - if (stop_ranging) { - session->stop_inband = true; - return; - } + fira_controlee_resync(session, sts_index, info->timestamp_dtu); + left_duration_dtu = + access->duration_dtu - offset_in_access_duration_dtu; - last_slot_index = 0; - for (i = 1; i < n_slots; i++) { - const struct fira_slot *slot = &local->slots[i]; - struct mcps802154_access_frame *frame = &local->frames[i]; - struct mcps802154_sts_params *sts_params = - &local->sts_params[i]; - bool is_tx; - u32 frame_dtu; - - is_tx = slot->tx_controlee_index != -1; - frame_dtu = info->timestamp_dtu + - session->params.slot_duration_dtu * slot->index; - last_slot_index = slot->index; - - fira_access_setup_frame(local, session, frame, sts_params, slot, - frame_dtu, is_tx); + if (left_duration_dtu < n_slots * params->slot_duration_dtu || + session->stop_inband) { + n_slots = 1; + } else { + int i; + + for (i = 1; i < n_slots; i++) { + const struct fira_slot *slot = &local->slots[i]; + struct mcps802154_access_frame *frame = + &local->frames[i]; + struct mcps802154_sts_params *sts_params = + &local->sts_params[i]; + bool is_tx; + u32 frame_dtu; + + is_tx = !slot->controller_tx; + frame_dtu = info->timestamp_dtu + + params->slot_duration_dtu * slot->index; + last_slot_index = slot->index; + + fira_access_setup_frame(local, session, frame, + sts_params, slot, frame_dtu, + is_tx); + } } - local->access.timestamp_dtu = info->timestamp_dtu; - local->access.duration_dtu = - session->params.slot_duration_dtu * (last_slot_index + 1); - local->access.n_frames = n_slots; - - session->next_block_stride_len = block_stride_len; + /* Trace the new (or not) session context and slots received. */ + trace_region_fira_rx_frame_control(local, session, left_duration_dtu, + n_slots); + /* Update the access. */ + access->duration_dtu = + offset_in_access_duration_dtu + + (last_slot_index + 1) * params->slot_duration_dtu; + access->n_frames = n_slots; return; + failed: - fira_ranging_info_set_status(ranging_info, - FIRA_STATUS_RANGING_RX_MAC_IE_DEC_FAILED, - slot->index); - local->access.timestamp_dtu = info->timestamp_dtu; - local->access.duration_dtu = session->params.slot_duration_dtu; + params = &local->current_session->params; + access->duration_dtu = + offset_in_access_duration_dtu + params->slot_duration_dtu; + fira_ranging_info_set_status( + ri, FIRA_STATUS_RANGING_RX_MAC_IE_DEC_FAILED, slot->index); } static void fira_rx_frame_measurement_report( @@ -398,8 +623,7 @@ static void fira_rx_frame_measurement_report( &local->ranging_info[slot->ranging_index]; struct mcps802154_ie_get_context ie_get = {}; - if (fira_frame_header_check_decrypt(local, slot, skb, &ie_get, NULL, - NULL)) + if (fira_frame_header_check_decrypt(local, slot, skb, &ie_get)) goto failed; if (!fira_frame_measurement_report_payload_check(local, slot, skb, @@ -422,8 +646,7 @@ fira_rx_frame_result_report(struct fira_local *local, &local->ranging_info[slot->ranging_index]; struct mcps802154_ie_get_context ie_get = {}; - if (fira_frame_header_check_decrypt(local, slot, skb, &ie_get, NULL, - NULL)) + if (fira_frame_header_check_decrypt(local, slot, skb, &ie_get)) goto failed; if (!fira_frame_result_report_payload_check(local, slot, skb, &ie_get)) @@ -456,6 +679,7 @@ static bool fira_do_process_rx_frame(enum mcps802154_rx_error_type error, break; case MCPS802154_RX_ERROR_UNCORRECTABLE: case MCPS802154_RX_ERROR_OTHER: + case MCPS802154_RX_ERROR_PHR_DECODE: status = FIRA_STATUS_RANGING_RX_PHY_DEC_FAILED; break; } @@ -469,12 +693,21 @@ static void fira_rx_frame(struct mcps802154_access *access, int frame_idx, enum mcps802154_rx_error_type error) { struct fira_local *local = access_to_local(access); + const struct fira_session_params *params; const struct fira_slot *slot = &local->slots[frame_idx]; - struct fira_session *session = local->current_session; - struct fira_ranging_info *ranging_info = + struct fira_ranging_info *ri = &local->ranging_info[slot->ranging_index]; + /* Don't initialize session before rx_frame_control. */ + struct fira_session *session; - if (fira_do_process_rx_frame(error, ranging_info, slot->index)) { + trace_region_fira_rx_frame(local->current_session, slot->message_id, + error); + + if (info && info->flags & MCPS802154_RX_FRAME_INFO_RSSI) { + ri->rx_rssis[ri->n_rx_rssis++] = + info->rssi < FIRA_RSSI_MAX ? info->rssi : FIRA_RSSI_MAX; + } + if (fira_do_process_rx_frame(error, ri, slot->index)) { switch (slot->message_id) { case FIRA_MESSAGE_ID_RANGING_INITIATION: case FIRA_MESSAGE_ID_RANGING_RESPONSE: @@ -497,25 +730,28 @@ static void fira_rx_frame(struct mcps802154_access *access, int frame_idx, WARN_UNREACHABLE_DEFAULT(); } } + /* Current session can change after call of rx_frame_control function. */ + session = local->current_session; + session->last_access_timestamp_dtu = access->timestamp_dtu; + params = &session->params; - if (skb) - kfree_skb(skb); - - trace_region_fira_rx_message( - session, slot->message_id, - local->ranging_info[slot->ranging_index].status); - - /* Controlee: Stop round on error. - Controller: Stop when all ranging fails. */ - if (local->ranging_info[slot->ranging_index].status) - if (session->params.device_type == FIRA_DEVICE_TYPE_CONTROLEE || + kfree_skb(skb); + fira_diagnostic(local, session, ri->rx_ctx, slot->index); + + /* + * Controlee: Stop round on error. + * Controller: Stop when all ranging fails. + */ + /* + * TODO: + * The usage of ri->status is hidden in function called. + * The reason of the end of access is not limpid. + */ + if (ri->status != FIRA_STATUS_RANGING_SUCCESS) { + if (params->device_type == FIRA_DEVICE_TYPE_CONTROLEE || (slot->message_id <= FIRA_MESSAGE_ID_RFRAME_MAX && --local->n_ranging_valid == 0)) access->n_frames = frame_idx + 1; - - if (session->params.device_type == FIRA_DEVICE_TYPE_CONTROLEE && - session->stop_inband) { - access->n_frames = frame_idx + 1; } } @@ -523,17 +759,19 @@ static struct sk_buff *fira_tx_get_frame(struct mcps802154_access *access, int frame_idx) { struct fira_local *local = access_to_local(access); + struct fira_session *session = local->current_session; + const struct fira_session_params *params = &session->params; const struct fira_slot *slot = &local->slots[frame_idx]; struct sk_buff *skb; - int rframe = local->current_session->params.rframe_config; - trace_region_fira_tx_message(local->current_session, slot->message_id); - if (rframe == FIRA_RFRAME_CONFIG_SP3 && + trace_region_fira_tx_get_frame(session, slot->message_id); + if (params->rframe_config == FIRA_RFRAME_CONFIG_SP3 && slot->message_id <= FIRA_MESSAGE_ID_RFRAME_MAX) return NULL; skb = mcps802154_frame_alloc(local->llhw, IEEE802154_MTU, GFP_KERNEL); - WARN_RETURN_ON(!skb, NULL); + if (!skb) + return NULL; fira_frame_header_put(local, slot, skb); @@ -575,11 +813,13 @@ static void fira_tx_return(struct mcps802154_access *access, int frame_idx, enum mcps802154_access_tx_return_reason reason) { struct fira_local *local = access_to_local(access); + struct fira_session *session = local->current_session; int i; kfree_skb(skb); /* Error on TX. */ + trace_region_fira_tx_return(session, reason); if (reason == MCPS802154_ACCESS_TX_RETURN_REASON_CANCEL) { for (i = 0; i < local->n_ranging_info; i++) { local->ranging_info[i].status = @@ -592,324 +832,330 @@ static void fira_access_done(struct mcps802154_access *access, bool error) { struct fira_local *local = access_to_local(access); struct fira_session *session = local->current_session; - int i; - - if (error) - for (i = 0; i < local->n_ranging_info; i++) - local->ranging_info[i].status = - FIRA_STATUS_RANGING_INTERNAL_ERROR; - fira_session_access_done(local, session, true); - - local->current_session = NULL; + u32 timestamp_dtu = access->timestamp_dtu; + + trace_region_fira_access_done(local, session, access->duration_dtu, + error); + fira_session_fsm_access_done(local, session, error); + fira_diagnostic_free(local); + if (!error) + /* No access are infinite normally. */ + timestamp_dtu += access->duration_dtu; + /* + * Must be call after FSM access done, because + * shared resource in local are used. + */ + fira_check_all_missed_ranging(local, session, timestamp_dtu); } -struct mcps802154_access_ops fira_access_ops = { - .common = { - .access_done = fira_access_done, - }, - .rx_frame = fira_rx_frame, - .tx_get_frame = fira_tx_get_frame, - .tx_return = fira_tx_return, -}; - static __le16 fira_access_set_short_address(struct fira_local *local, - struct fira_session *session, + const struct fira_session *session, struct mcps802154_access *access) { + const struct fira_session_params *params = &session->params; __le16 src_short_addr = mcps802154_get_short_addr(local->llhw); - if (session->params.short_addr != IEEE802154_ADDR_SHORT_BROADCAST && - src_short_addr != session->params.short_addr) { + if (params->short_addr != IEEE802154_ADDR_SHORT_BROADCAST && + src_short_addr != params->short_addr) { access->hw_addr_filt = (struct ieee802154_hw_addr_filt){ - .short_addr = session->params.short_addr, + .short_addr = params->short_addr, }; access->hw_addr_filt_changed = IEEE802154_AFILT_SADDR_CHANGED; - return session->params.short_addr; - } else { - access->hw_addr_filt_changed = 0; - return src_short_addr; + return params->short_addr; } + access->hw_addr_filt_changed = 0; + return src_short_addr; } -static const struct mcps802154_channel * -fira_access_channel(struct fira_local *local, - const struct fira_session *session) +static struct mcps802154_access_ops fira_controller_access_ops = { + .common = { + .access_done = fira_access_done, + }, + .rx_frame = fira_rx_frame, + .tx_get_frame = fira_tx_get_frame, + .tx_return = fira_tx_return, +}; + +int fira_session_get_slot_count(const struct fira_session *session) { - if (session->params.channel_number || - session->params.preamble_code_index) { - const struct mcps802154_channel *channel = - mcps802154_get_current_channel(local->llhw); - - local->channel = *channel; - if (session->params.channel_number) - local->channel.channel = session->params.channel_number; - if (session->params.preamble_code_index) - local->channel.preamble_code = - session->params.preamble_code_index; - return &local->channel; + const struct fira_session_params *params = &session->params; + int nb_controlee = fira_session_controlees_running_count(session); + /* Control frame. */ + int slot_count = 1; + + if (nb_controlee) { + /* Ranging initiation frame. */ + slot_count++; + /* Ranging response frame(s). */ + slot_count += nb_controlee; + /* Ranging final frame. */ + if (params->ranging_round_usage == + FIRA_RANGING_ROUND_USAGE_DSTWR) + slot_count++; + /* Measurement report frame. */ + slot_count++; + /* Result report frame(s). */ + slot_count += nb_controlee; } - - return NULL; + return slot_count; } -static struct mcps802154_access * -fira_access_controller(struct fira_local *local, struct fira_session *session) +struct mcps802154_access * +fira_get_access_controller(struct fira_local *local, + const struct fira_session_demand *fsd) { - struct mcps802154_access *access; + struct fira_session *session = local->current_session; + const struct fira_session_params *params = &session->params; + const struct fira_measurement_sequence_step *step = + fira_session_get_meas_seq_step(session); + int slots_per_block = + params->block_duration_dtu / params->slot_duration_dtu; + struct mcps802154_access *access = &local->access; struct mcps802154_access_frame *frame; struct mcps802154_sts_params *sts_params; - struct fira_slot *s; struct fira_ranging_info *ri; - struct fira_controlees_array *controlees_array = - &session->current_controlees; - const struct fira_measurement_sequence_data *meas_seq = - &session->params.meas_seq; - const struct fira_measurement_sequence_step *current_ms_step = - &meas_seq->active->steps[meas_seq->current_step]; - int i, j; + struct fira_slot *s; u32 frame_dtu; int index = 0; - bool double_sided = (session->params.ranging_round_usage == - FIRA_RANGING_ROUND_USAGE_DSTWR); + int i; + struct fira_controlee *controlee; + + trace_region_fira_get_access_controller(local, session, fsd); - access = &local->access; + /* Update local context (shared memory used by all sessions). */ local->src_short_addr = fira_access_set_short_address(local, session, access); - local->dst_short_addr = (controlees_array->size == 1) ? - controlees_array->data[0].short_addr : - IEEE802154_ADDR_SHORT_BROADCAST; - - access->method = MCPS802154_ACCESS_METHOD_MULTI; - access->ops = &fira_access_ops; - access->timestamp_dtu = session->block_start_dtu + - fira_session_get_round_slot(session) * - session->params.slot_duration_dtu; - access->frames = local->frames; - access->channel = fira_access_channel(local, session); - - local->n_stopped_controlees_short_addr = 0; - for (i = 0; i < controlees_array->size; i++) { - const struct fira_controlee *c = &controlees_array->data[i]; - - if (c->state != FIRA_CONTROLEE_STATE_RUNNING) { - local->stopped_controlees_short_addr - [local->n_stopped_controlees_short_addr] = - c->short_addr; - local->n_stopped_controlees_short_addr++; - } + local->dst_short_addr = + session->n_current_controlees == 1 ? + list_first_entry(&session->current_controlees, + struct fira_controlee, entry) + ->short_addr : + IEEE802154_ADDR_SHORT_BROADCAST; + + /* Update session. */ + session->last_access_timestamp_dtu = fsd->timestamp_dtu; + session->block_start_dtu = fsd->block_start_dtu; + session->block_index += fsd->add_blocks; + session->block_stride_len = params->block_stride_len; + session->sts_index += fsd->add_blocks * slots_per_block; + session->round_index = fsd->round_index; + session->controller.next_block_index = + session->block_index + session->block_stride_len + 1; + session->next_round_index = + params->round_hopping ? + fira_round_hopping_sequence_get( + session, session->controller.next_block_index) : + 0; + + /* Build number of controlee which are stopped. */ + local->n_stopped_controlees = 0; + list_for_each_entry (controlee, &session->current_controlees, entry) { + if (controlee->state == FIRA_CONTROLEE_STATE_STOPPING || + controlee->state == FIRA_CONTROLEE_STATE_DELETING) + local->stopped_controlees[local->n_stopped_controlees++] = + controlee->short_addr; } + /* Build number of controlee which are running. */ + local->n_ranging_valid = + session->n_current_controlees - local->n_stopped_controlees; - local->n_ranging_valid = local->n_ranging_info = - controlees_array->size - local->n_stopped_controlees_short_addr; + /* Reset 'n' ranging info to store info related to controlees. */ + local->n_ranging_info = local->n_ranging_valid; ri = local->ranging_info; + memset(ri, 0, + local->n_ranging_valid * sizeof(struct fira_ranging_info)); - memset(ri, 0, local->n_ranging_info * sizeof(*ri)); - + /* Prepare control message slot for fira_rx_frame. */ s = local->slots; - s->index = index++; - s->tx_controlee_index = -1; + s->controller_tx = true; s->ranging_index = 0; - s->tx_ant_set = current_ms_step->tx_ant_set_nonranging; + s->tx_ant_set = step->tx_ant_set_nonranging; s->message_id = FIRA_MESSAGE_ID_CONTROL; + s->controlee = NULL; s++; - + /* Prepare other slots. */ if (local->n_ranging_info) { s->index = index++; - s->tx_controlee_index = -1; + s->controller_tx = true; s->ranging_index = 0; - s->tx_ant_set = current_ms_step->tx_ant_set_ranging; + s->tx_ant_set = step->tx_ant_set_ranging; s->message_id = FIRA_MESSAGE_ID_RANGING_INITIATION; + s->controlee = NULL; s++; - for (i = 0, j = 0; i < controlees_array->size; i++) { - if (controlees_array->data[i].state != - FIRA_CONTROLEE_STATE_RUNNING) + i = 0; + list_for_each_entry (controlee, &session->current_controlees, + entry) { + if (!fira_session_controlee_active(controlee)) continue; - ri->short_addr = controlees_array->data[i].short_addr; + ri->short_addr = controlee->short_addr; + ri->rx_ctx = session->rx_ctx[i]; /* Requested in fira_report_aoa function. */ ri++; s->index = index++; - s->tx_controlee_index = i; - s->ranging_index = j++; + s->controller_tx = false; + s->ranging_index = i++; s->rx_ant_set = fira_session_get_rx_ant_set( session, FIRA_MESSAGE_ID_RANGING_RESPONSE); s->message_id = FIRA_MESSAGE_ID_RANGING_RESPONSE; + s->controlee = controlee; s++; } - if (double_sided) { + if (params->ranging_round_usage == + FIRA_RANGING_ROUND_USAGE_DSTWR) { s->index = index++; - s->tx_controlee_index = -1; + s->controller_tx = true; s->ranging_index = 0; - s->tx_ant_set = current_ms_step->tx_ant_set_ranging; + s->tx_ant_set = step->tx_ant_set_ranging; s->message_id = FIRA_MESSAGE_ID_RANGING_FINAL; + s->controlee = NULL; s++; } s->index = index++; - s->tx_controlee_index = -1; + s->controller_tx = true; s->ranging_index = 0; - s->tx_ant_set = current_ms_step->tx_ant_set_nonranging; + s->tx_ant_set = step->tx_ant_set_nonranging; s->message_id = FIRA_MESSAGE_ID_MEASUREMENT_REPORT; + s->controlee = NULL; s++; - if (session->params.result_report_phase) { - for (i = 0, j = 0; i < controlees_array->size; i++) { - if (controlees_array->data[i].state != - FIRA_CONTROLEE_STATE_RUNNING) + if (params->result_report_phase) { + i = 0; + list_for_each_entry (controlee, + &session->current_controlees, + entry) { + if (!fira_session_controlee_active(controlee)) continue; s->index = index++; - s->tx_controlee_index = i; - s->ranging_index = j++; - s->rx_ant_set = - current_ms_step->rx_ant_set_nonranging; + s->controller_tx = false; + s->ranging_index = i++; + s->rx_ant_set = step->rx_ant_set_nonranging; s->message_id = FIRA_MESSAGE_ID_RESULT_REPORT; + s->controlee = controlee; s++; } } } - access->n_frames = index; - - frame_dtu = access->timestamp_dtu; - - for (i = 0; i < access->n_frames; i++) { - bool is_tx; - + /* Configure frames for fproc. */ + frame_dtu = fsd->timestamp_dtu; + for (i = 0; i < index; i++) { s = &local->slots[i]; frame = &local->frames[i]; sts_params = &local->sts_params[i]; - is_tx = s->tx_controlee_index == -1; - fira_access_setup_frame(local, session, frame, sts_params, s, - frame_dtu, is_tx); + frame_dtu, s->controller_tx); - frame_dtu += session->params.slot_duration_dtu; + frame_dtu += params->slot_duration_dtu; } - access->duration_dtu = frame_dtu - access->timestamp_dtu; + /* + * Configure the access. + * 'duration_dtu' can be decrease on reception error. + */ + access->ops = &fira_controller_access_ops; + access->timestamp_dtu = fsd->timestamp_dtu; + access->duration_dtu = frame_dtu - fsd->timestamp_dtu; + access->n_frames = index; return access; } -static struct mcps802154_access * -fira_access_controlee(struct fira_local *local, struct fira_session *session) +static struct mcps802154_access_ops fira_controlee_access_ops = { + .common = { + .access_done = fira_access_done, + }, + .rx_frame = fira_rx_frame, + .tx_get_frame = fira_tx_get_frame, + .tx_return = fira_tx_return, +}; + +struct mcps802154_access * +fira_get_access_controlee(struct fira_local *local, + const struct fira_session_demand *fsd) { - struct mcps802154_access *access; + /* + * \ Important: + * '-.__.-' It's almost forbidden to update session + * /oo |--.--,--,--. content for a controlee. Because, the + * \_.-'._i__i__i_.' session can change on control frame received. + * """"""""" + */ + struct fira_session *session = local->current_session; + const struct fira_session_params *params = &session->params; + struct mcps802154_access *access = &local->access; struct mcps802154_access_frame *frame; - struct fira_slot *s; struct fira_ranging_info *ri; - int timeout_dtu; + struct fira_slot *s; + u16 request = MCPS802154_RX_FRAME_INFO_TIMESTAMP_DTU; + + trace_region_fira_get_access_controlee(local, session, fsd); - access = &local->access; + /* Update local context (shared memory used by all sessions). */ local->src_short_addr = fira_access_set_short_address(local, session, access); - local->dst_short_addr = session->params.controller_short_addr; - - access->method = MCPS802154_ACCESS_METHOD_MULTI; - access->ops = &fira_access_ops; - access->timestamp_dtu = session->last_access_timestamp_dtu; - access->duration_dtu = session->last_access_duration_dtu; - access->frames = local->frames; - access->n_frames = 1; - access->channel = fira_access_channel(local, session); - - ri = local->ranging_info; - memset(ri, 0, sizeof(*ri)); - ri->short_addr = session->params.controller_short_addr; + local->dst_short_addr = params->controller_short_addr; + local->n_stopped_controlees = 0; + + /* + * Update session. + * Updated values are used in case of bad reception (timeout/error). + * Otherwise on good reception, many are override. + * by the controlee resync function. + */ + session->block_start_dtu = fsd->block_start_dtu; + session->block_index += fsd->add_blocks; + + /* Reset one ranging info to store info related to the controller. */ local->n_ranging_info = 1; - local->n_stopped_controlees_short_addr = 0; - + ri = local->ranging_info; + memset(ri, 0, sizeof(struct fira_ranging_info)); + /* + * Warning: + * - short_addr is used when rx control have an error. + * - Be careful to not initialize too much in ri, because + * session can change on rx control. + */ + ri->short_addr = params->controller_short_addr; + + /* Prepare control message slot for fira_rx_frame. */ s = local->slots; s->index = 0; - s->tx_controlee_index = -1; + s->controller_tx = true; s->ranging_index = 0; - s->rx_ant_set = fira_session_get_current_meas_seq_step(session) - ->rx_ant_set_nonranging; + s->rx_ant_set = + fira_session_get_meas_seq_step(session)->rx_ant_set_nonranging; s->message_id = FIRA_MESSAGE_ID_CONTROL; + s->controlee = NULL; - if (session->synchronised) - timeout_dtu = 2 * fira_session_get_block_duration_margin( - local, session); - else if (!access->duration_dtu) - timeout_dtu = -1; - else - timeout_dtu = access->duration_dtu - - session->params.round_duration_slots * - session->params.slot_duration_dtu; - + /* Configure frames for fproc. */ + if (params->report_rssi) + request |= MCPS802154_RX_FRAME_INFO_RSSI; frame = local->frames; *frame = (struct mcps802154_access_frame){ .is_tx = false, .rx = { - .info = { - .timestamp_dtu = access->timestamp_dtu, - .timeout_dtu = timeout_dtu, - .flags = MCPS802154_RX_INFO_TIMESTAMP_DTU | - MCPS802154_RX_INFO_RANGING_ROUND, + .frame_config = { + .timestamp_dtu = fsd->timestamp_dtu, + .timeout_dtu = fsd->rx_timeout_dtu, + .flags = MCPS802154_RX_FRAME_CONFIG_RANGING_ROUND | + MCPS802154_RX_FRAME_CONFIG_TIMESTAMP_DTU, .ant_set_id = s->rx_ant_set, }, - .frame_info_flags_request - = MCPS802154_RX_FRAME_INFO_TIMESTAMP_DTU, + .frame_info_flags_request = request, }, }; + /* + * Configure the access. + * 'duration_dtu' will be overridden on control frame reception. + */ + access->ops = &fira_controlee_access_ops; + access->timestamp_dtu = fsd->timestamp_dtu; + access->duration_dtu = fsd->max_duration_dtu; + access->n_frames = 1; return access; } - -struct mcps802154_access *fira_get_access(struct mcps802154_region *region, - u32 next_timestamp_dtu, - int next_in_region_dtu, - int region_duration_dtu) -{ - struct fira_local *local = region_to_local(region); - struct fira_session *session; - - if (!local->current_session) { - session = fira_session_next( - local, next_timestamp_dtu + local->llhw->anticip_dtu, - region_duration_dtu - next_in_region_dtu); - local->current_session = session; - } else { - session = local->current_session; - } - - if (!session) - return NULL; - return fira_compute_access(local, session); -} - -struct mcps802154_access *fira_compute_access(struct fira_local *local, - struct fira_session *session) -{ - fira_session_prepare(session); - if (session->params.device_type == FIRA_DEVICE_TYPE_CONTROLLER) - return fira_access_controller(local, session); - else - return fira_access_controlee(local, session); -} - -void fira_session_get_demand(struct fira_local *local, - struct fira_session *session, - struct mcps802154_region_demand *demand) -{ - u32 base_timestamp_dtu = session->block_start_dtu + - fira_session_get_round_slot(session) * - session->params.slot_duration_dtu; - if (session->params.device_type == FIRA_DEVICE_TYPE_CONTROLLER) { - demand->timestamp_dtu = base_timestamp_dtu; - demand->max_duration_dtu = - (4 + 2 * session->current_controlees.size) * - session->params.slot_duration_dtu; - } else { - int block_duration_margin_dtu = - fira_session_get_block_duration_margin(local, session); - demand->timestamp_dtu = - base_timestamp_dtu - block_duration_margin_dtu; - demand->max_duration_dtu = - session->params.round_duration_slots * - session->params.slot_duration_dtu + - block_duration_margin_dtu; - } -} diff --git a/mac/fira_access.h b/mac/fira_access.h index 43812ba..3b6ca8c 100644 --- a/mac/fira_access.h +++ b/mac/fira_access.h @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2020-2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. @@ -26,41 +26,39 @@ #include <net/mcps802154_schedule.h> +/* Forward declaration. */ struct fira_local; struct fira_session; +struct fira_session_demand; /** - * fira_get_access() - Get access for a given region at the given timestamp. - * @region: Region. - * @next_timestamp_dtu: Next access opportunity. - * @next_in_region_dtu: Unused. - * @region_duration_dtu: Unused. + * fira_session_get_slot_count() - Return the number of slot for the session. + * @session: FiRa session context. * - * Return: The access. + * Return: Number of slot. */ -struct mcps802154_access *fira_get_access(struct mcps802154_region *region, - u32 next_timestamp_dtu, - int next_in_region_dtu, - int region_duration_dtu); +int fira_session_get_slot_count(const struct fira_session *session); /** - * fira_compute_access() - Get access for a given session. - * @local: FiRa context. - * @session: Session. + * fira_get_access_controller() - Build the access for controller. + * @local: FiRa region context. + * @fsd: FiRa Session Demand from the get_demand of the session fsm. * - * Return: The access. + * Return: A valid access. */ -struct mcps802154_access *fira_compute_access(struct fira_local *local, - struct fira_session *session); +struct mcps802154_access * +fira_get_access_controller(struct fira_local *local, + const struct fira_session_demand *fsd); /** - * fira_session_get_demand() - Get access information for a given session. - * @local: FiRa context. - * @session: Session. - * @demand: Access information. + * fira_get_access_controlee() - Build the access for controlee. + * @local: FiRa region context. + * @fsd: FiRa Session Demand from the get_demand of the session fsm. + * + * Return: A valid access. */ -void fira_session_get_demand(struct fira_local *local, - struct fira_session *session, - struct mcps802154_region_demand *demand); +struct mcps802154_access * +fira_get_access_controlee(struct fira_local *local, + const struct fira_session_demand *fsd); #endif /* FIRA_ACCESS_H */ diff --git a/mac/fira_aead.h b/mac/fira_aead.h index 11baf70..8b9c968 100644 --- a/mac/fira_aead.h +++ b/mac/fira_aead.h @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2020-2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. diff --git a/mac/fira_cmac.h b/mac/fira_cmac.h index 464eb72..112874d 100644 --- a/mac/fira_cmac.h +++ b/mac/fira_cmac.h @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2020-2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. diff --git a/mac/fira_crypto.c b/mac/fira_crypto.c index 56f8299..35297e0 100644 --- a/mac/fira_crypto.c +++ b/mac/fira_crypto.c @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2020-2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. diff --git a/mac/fira_crypto.h b/mac/fira_crypto.h index 88b4609..fa8d524 100644 --- a/mac/fira_crypto.h +++ b/mac/fira_crypto.h @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2020-2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. diff --git a/mac/fira_frame.c b/mac/fira_frame.c index 1b4cf4c..d8e2546 100644 --- a/mac/fira_frame.c +++ b/mac/fira_frame.c @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2020-2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. @@ -64,7 +64,7 @@ #define FIRA_MIC_LEVEL 64 #define FIRA_MIC_LEN (FIRA_MIC_LEVEL / 8) -/* 3 IE headers in the frame : vendor IE, header terminator and payload */ +/* 3 IE headers in the frame : vendor IE, header terminator and payload. */ #define FIRA_FRAME_WITHOUT_PAYLOAD_LEN \ (IEEE802154_FC_LEN + IEEE802154_SCF_LEN + IEEE802154_SHORT_ADDR_LEN + \ 3 * IEEE802154_IE_HEADER_LEN + FIRA_IE_HEADER_LEN + FIRA_MIC_LEN + \ @@ -91,22 +91,26 @@ #define FIRA_RESULT_REPORT_CONTROL_AOA_ELEVATION_PRESENT (1 << 2) #define FIRA_RESULT_REPORT_CONTROL_AOA_FOM_PRESENT (1 << 3) -bool fira_frame_check_n_controlees(struct fira_session *session, +bool fira_frame_check_n_controlees(const struct fira_session *session, size_t n_controlees, bool active) { - /* TODO: use more parameters (embedded mode, ranging mode, device + /* + * TODO: use more parameters (embedded mode, ranging mode, device * type...) to calculate the size of frames. * Currently only SS-TWR vs DS-TWR mode is considered. * The computation MUST stay "pessimistic" (aka strict). - * E.g.: for RCM each new controlee consumes 8 bytes so we need - * AT LEAST 8 * n_controlee bytes of "free space". */ - struct fira_session_params *params = &session->params; + * E.g.: for control frame, each new controlee consumes 8 bytes so + * we need AT LEAST 8 * n_controlee bytes of "free space". + */ + const struct fira_session_params *params = &session->params; size_t mrm_size, rcm_size; size_t n_msg_controller; size_t n_msg_controlee = 2; + if (n_controlees > FIRA_CONTROLEES_MAX) + return false; if (!active) - return n_controlees <= FIRA_CONTROLEES_MAX; + return true; if (params->ranging_round_usage == FIRA_RANGING_ROUND_USAGE_DSTWR) { mrm_size = FIRA_FRAME_WITHOUT_PAYLOAD_LEN + @@ -185,8 +189,7 @@ void fira_frame_control_payload_put(const struct fira_local *local, u8 *p; int i; - n_mngt = local->access.n_frames - 1 + - local->n_stopped_controlees_short_addr; + n_mngt = local->access.n_frames - 1 + local->n_stopped_controlees; p = fira_frame_common_payload_put(skb, FIRA_IE_PAYLOAD_CONTROL_LEN(n_mngt), @@ -194,18 +197,15 @@ void fira_frame_control_payload_put(const struct fira_local *local, *p++ = n_mngt; *p++ = 0; - *p++ = session->next_block_stride_len; + *p++ = session->block_stride_len; for (i = 0; i < local->access.n_frames - 1; i++) { const struct fira_slot *slot = &local->slots[i + 1]; - int initiator = slot->tx_controlee_index == -1; + int initiator = slot->controller_tx; int slot_index = slot->index; - __le16 short_addr = - slot->tx_controlee_index == -1 ? - local->src_short_addr : - session->current_controlees - .data[slot->tx_controlee_index] - .short_addr; + __le16 short_addr = slot->controller_tx ? + local->src_short_addr : + slot->controlee->short_addr; int message_id = slot->message_id; u32 mngt = FIELD_PREP(FIRA_MNGT_RANGING_ROLE, initiator) | FIELD_PREP(FIRA_MNGT_SLOT_INDEX, slot_index) | @@ -215,8 +215,8 @@ void fira_frame_control_payload_put(const struct fira_local *local, p += sizeof(u32); } - for (i = 0; i < local->n_stopped_controlees_short_addr; i++) { - __le16 short_addr = local->stopped_controlees_short_addr[i]; + for (i = 0; i < local->n_stopped_controlees; i++) { + __le16 short_addr = local->stopped_controlees[i]; u32 mngt = FIELD_PREP(FIRA_MNGT_SHORT_ADDR, short_addr) | FIELD_PREP(FIRA_MNGT_STOP, 1); put_unaligned_le32(mngt, p); @@ -229,10 +229,11 @@ void fira_frame_measurement_report_payload_put(const struct fira_local *local, struct sk_buff *skb) { const struct fira_session *session = local->current_session; + const struct fira_session_params *params = &session->params; const struct fira_ranging_info *ranging_info = &local->ranging_info[slot->ranging_index]; u8 *p; - int hopping_mode = session->params.round_hopping; + int hopping_mode = params->round_hopping; int round_index_present = 1; int reply_time_present = 0; /* for initiator */ int n_reply_time = local->n_ranging_valid; @@ -240,8 +241,8 @@ void fira_frame_measurement_report_payload_put(const struct fira_local *local, u32 first_round_trip_time; u32 reply_time; u64 initiation_rctu, response_rctu, final_rctu; - bool double_sided = (session->params.ranging_round_usage == - FIRA_RANGING_ROUND_USAGE_DSTWR); + bool double_sided = params->ranging_round_usage == + FIRA_RANGING_ROUND_USAGE_DSTWR; p = fira_frame_common_payload_put( skb, @@ -267,10 +268,12 @@ void fira_frame_measurement_report_payload_put(const struct fira_local *local, put_unaligned_le16(session->next_round_index, p); p += sizeof(u16); - /* No handling for failed measurement, as there is only one, a failed + /* + * No handling for failed measurement, as there is only one, a failed * measurement will cancel the ranging round. * With several measurements, make sure a later measurement can still be - * done if an earlier one is failed. */ + * done if an earlier one is failed. + */ initiation_rctu = ranging_info ->timestamps_rctu[FIRA_MESSAGE_ID_RANGING_INITIATION]; @@ -378,22 +381,21 @@ void fira_frame_rframe_payload_put(struct fira_local *local, struct sk_buff *skb) { struct fira_session *session = local->current_session; - struct fira_session_params *params = &local->current_session->params; + const struct fira_session_params *params = &session->params; u8 *p; - if (params->data_payload_len == 0) + if (session->data_payload.seq == params->data_payload_seq) return; p = mcps802154_ie_put_payload_ie(skb, IEEE802154_IE_PAYLOAD_VENDOR_GID, FIRA_IE_VENDOR_OUI_LEN + params->data_payload_len); WARN_RETURN_VOID_ON(!p); - put_unaligned_le24(params->data_vendor_oui, p); p += FIRA_IE_VENDOR_OUI_LEN; memcpy(p, params->data_payload, params->data_payload_len); - params->data_payload_len = 0; - session->data_payload_seq_sent = params->data_payload_seq; + session->data_payload.seq = params->data_payload_seq; + session->data_payload.sent = true; } bool fira_frame_header_check(const struct fira_local *local, @@ -461,8 +463,8 @@ static bool fira_frame_control_read(struct fira_local *local, u8 *p, int n_mngt, i; u16 msg_ids = 0; bool stop_found = false; - const struct fira_measurement_sequence_step *current_ms_step = - fira_session_get_current_meas_seq_step(session); + const struct fira_measurement_sequence_step *step = + fira_session_get_meas_seq_step(session); n_mngt = *p++; if (ie_len < FIRA_IE_PAYLOAD_CONTROL_LEN(n_mngt)) @@ -517,19 +519,13 @@ static bool fira_frame_control_read(struct fira_local *local, u8 *p, msg_ids |= msg_id; if (slot == local->slots + FIRA_CONTROLEE_FRAMES_MAX) return false; - if (!initiator) - last.tx_controlee_index = 0; - else - last.tx_controlee_index = -1; + last.controller_tx = initiator; last.ranging_index = 0; last.message_id = message_id; if (!initiator) { last.tx_ant_set = - is_rframe ? - current_ms_step - ->tx_ant_set_ranging : - current_ms_step - ->tx_ant_set_nonranging; + is_rframe ? step->tx_ant_set_ranging : + step->tx_ant_set_nonranging; } else { last.rx_ant_set = fira_session_get_rx_ant_set( session, message_id); @@ -604,13 +600,13 @@ fira_frame_measurement_report_fill_ranging_info(struct fira_local *local, u64 rx_initiation_rctu, tx_response_rctu, rx_final_rctu; u32 local_round_trip_rctu, local_reply_rctu; int tof_rctu, i; - bool double_sided = (session->params.ranging_round_usage == - FIRA_RANGING_ROUND_USAGE_DSTWR); + bool double_sided = session->params.ranging_round_usage == + FIRA_RANGING_ROUND_USAGE_DSTWR; control = *p++; - hopping_mode = - !!(control & FIRA_MEASUREMENT_REPORT_CONTROL_HOPPING_MODE); - round_index_present = !!( - control & FIRA_MEASUREMENT_REPORT_CONTROL_ROUND_INDEX_PRESENT); + hopping_mode = FIELD_GET(FIRA_MEASUREMENT_REPORT_CONTROL_HOPPING_MODE, + control); + round_index_present = FIELD_GET( + FIRA_MEASUREMENT_REPORT_CONTROL_ROUND_INDEX_PRESENT, control); n_reply_time = FIELD_GET(FIRA_MEASUREMENT_REPORT_CONTROL_N_REPLY_TIME, control); @@ -634,13 +630,13 @@ fira_frame_measurement_report_fill_ranging_info(struct fira_local *local, n_reply_time))) return false; - session->hopping_sequence_generation = hopping_mode && - !round_index_present; if (round_index_present) { int next_round_index; next_round_index = get_unaligned_le16(p); p += sizeof(u16); + + session->controlee.next_round_index_valid = true; session->next_round_index = next_round_index; } @@ -702,6 +698,7 @@ fira_frame_measurement_report_fill_ranging_info(struct fira_local *local, ranging_info->tof_rctu = tof_rctu > 0 ? tof_rctu : 0; ranging_info->tof_present = true; + session->controlee.hopping_mode = hopping_mode; return true; } @@ -709,13 +706,14 @@ bool fira_frame_measurement_report_payload_check( struct fira_local *local, const struct fira_slot *slot, struct sk_buff *skb, struct mcps802154_ie_get_context *ie_get) { + const struct fira_session *session = local->current_session; + const struct fira_session_params *params = &session->params; bool fira_payload_seen = false; unsigned int minimum_payload_len; int r; u8 *p; - if (local->current_session->params.ranging_round_usage == - FIRA_RANGING_ROUND_USAGE_DSTWR) + if (params->ranging_round_usage == FIRA_RANGING_ROUND_USAGE_DSTWR) minimum_payload_len = FIRA_IE_PAYLOAD_MEASUREMENT_REPORT_TYPE1_LEN(false, 0); else @@ -857,9 +855,10 @@ bool fira_frame_rframe_payload_check(struct fira_local *local, struct sk_buff *skb, struct mcps802154_ie_get_context *ie_get) { + const struct fira_session *session = local->current_session; + const struct fira_session_params *params = &session->params; struct fira_ranging_info *ranging_info = &local->ranging_info[slot->ranging_index]; - struct fira_session_params *params = &local->current_session->params; bool rframe_payload_seen = false; int r; u8 *p; @@ -916,56 +915,82 @@ int fira_frame_decrypt(struct fira_local *local, struct fira_session *session, { __le16 src_short_addr; - if (slot->tx_controlee_index == -1) + if (slot->controller_tx) src_short_addr = local->dst_short_addr; else - src_short_addr = session->current_controlees - .data[slot->tx_controlee_index] - .short_addr; + src_short_addr = slot->controlee->short_addr; return fira_aead_decrypt(&session->crypto.aead, skb, header_len, src_short_addr, slot->index); } +struct fira_session *fira_rx_frame_control_header_check( + struct fira_local *local, const struct fira_slot *slot, + struct sk_buff *skb, struct mcps802154_ie_get_context *ie_get, + u32 *sts_index) +{ + const struct fira_session *session = local->current_session; + struct fira_session *session_found = NULL; + u32 session_id; + + if (!fira_frame_header_check(local, skb, ie_get, sts_index, + &session_id)) + return NULL; + if (session->id == session_id) { + session_found = local->current_session; + } else if (session->controlee.synchronised) { + return NULL; + } else { + session_found = + fira_get_session_by_session_id(local, session_id); + if (!session_found || + session_found->params.device_type != + FIRA_DEVICE_TYPE_CONTROLEE || + !fira_session_is_active(session_found)) + return NULL; + /* + * FIXME: The previous session will not sent a ranging round + * report failure. + * + * The most simple is probably to remove a round ranging? + * or keep somewhere, previous value. + * or choice number 3. + * ``` + * int remove_blocks = session->block_stride_len + 1; + * + * session->block_start_dtu -= remove_blocks * + * params->block_duration_dtu; + * session->block_index -= remove_blocks; + * ``` + */ + } + /* Update current and allow content of session to be updated. */ + local->current_session = session_found; + return session_found; +} + int fira_frame_header_check_decrypt(struct fira_local *local, const struct fira_slot *slot, struct sk_buff *skb, - struct mcps802154_ie_get_context *ie_get, - u32 *allow_resync_sts_index, - struct fira_session **allow_resync_session) + struct mcps802154_ie_get_context *ie_get) { struct fira_session *session = local->current_session; + unsigned header_len; u32 sts_index; u32 session_id; u8 *header; - unsigned int header_len; - bool active; header = skb->data; if (!fira_frame_header_check(local, skb, ie_get, &sts_index, &session_id)) return -EBADMSG; - - if (allow_resync_session && session_id != session->id) { - session = fira_session_get(local, session_id, &active); - if (!session || - session->params.device_type != FIRA_DEVICE_TYPE_CONTROLEE || - !active || session->synchronised) - return -EBADMSG; - *allow_resync_session = session; - } else if (session_id != session->id) { + if (session_id != session->id) return -EBADMSG; - } - - if (allow_resync_sts_index) { - *allow_resync_sts_index = sts_index - slot->index; - } else if (sts_index != - fira_session_get_round_sts_index(session) + slot->index) { + if (sts_index != + fira_session_get_round_sts_index(session) + slot->index) return -EBADMSG; - } header_len = skb->data - header; - return fira_frame_decrypt(local, session, slot, skb, header_len); } diff --git a/mac/fira_frame.h b/mac/fira_frame.h index 33bf1f2..1638e06 100644 --- a/mac/fira_frame.h +++ b/mac/fira_frame.h @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2020-2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. @@ -47,7 +47,7 @@ struct fira_session_params; * For an active session, it depends on the space left in messages, which is * determined by the session parameters. */ -bool fira_frame_check_n_controlees(struct fira_session *session, +bool fira_frame_check_n_controlees(const struct fira_session *session, size_t n_controlees, bool active); /** @@ -202,24 +202,34 @@ int fira_frame_decrypt(struct fira_local *local, struct fira_session *session, unsigned int header_len); /** + * fira_rx_frame_control_header_check() - Check control frame and consume + * header. + * @local: FiRa context. + * @slot: Corresponding slot. + * @skb: Frame buffer. + * @ie_get: Context used to read IE, must be zero initialized. + * @sts_index: STS index received. + * + * Return: Session context or NULL. + */ +struct fira_session *fira_rx_frame_control_header_check( + struct fira_local *local, const struct fira_slot *slot, + struct sk_buff *skb, struct mcps802154_ie_get_context *ie_get, + u32 *sts_index); + +/** * fira_frame_header_check_decrypt() - Check and consume header, and decrypt * payload. * @local: FiRa context. * @slot: Corresponding slot. * @skb: Frame buffer. * @ie_get: Context used to read IE, must be zero initialized. - * @allow_resync_sts_index: If not NULL, allow STS index resynchronisation and - * store received STS index at given address, if NULL, forbid resynchronisation. - * @allow_resync_session: If not NULL, allow session synchronisation and store received - * session at given address, if NULL, forbid resynchronisation. * * Return: 0 or error. */ int fira_frame_header_check_decrypt(struct fira_local *local, const struct fira_slot *slot, struct sk_buff *skb, - struct mcps802154_ie_get_context *ie_get, - u32 *allow_resync_sts_index, - struct fira_session **allow_resync_session); + struct mcps802154_ie_get_context *ie_get); #endif /* FIRA_FRAME_H */ diff --git a/mac/fira_region.c b/mac/fira_region.c index 7924c6f..6e89bb1 100644 --- a/mac/fira_region.c +++ b/mac/fira_region.c @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2020-2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. @@ -22,10 +22,8 @@ */ #include <linux/slab.h> -#include <linux/errno.h> -#include <linux/math64.h> - #include <linux/netdevice.h> +#include <linux/errno.h> #include <net/mcps802154_schedule.h> #include <net/fira_region_nl.h> @@ -39,6 +37,22 @@ static struct mcps802154_region_ops fira_region_ops; +static void fira_report_event(struct work_struct *work) +{ + struct fira_local *local = + container_of(work, struct fira_local, report_work); + struct sk_buff *skb; + int r; + + while (!skb_queue_empty(&local->report_queue)) { + skb = skb_dequeue(&local->report_queue); + r = mcps802154_region_event(local->llhw, skb); + if (r == -ECONNREFUSED) + /* TODO stop. */ + ; + } +} + static struct mcps802154_region *fira_open(struct mcps802154_llhw *llhw) { struct fira_local *local; @@ -48,9 +62,11 @@ static struct mcps802154_region *fira_open(struct mcps802154_llhw *llhw) return NULL; local->llhw = llhw; local->region.ops = &fira_region_ops; - local->current_session = NULL; INIT_LIST_HEAD(&local->inactive_sessions); INIT_LIST_HEAD(&local->active_sessions); + skb_queue_head_init(&local->report_queue); + INIT_WORK(&local->report_work, fira_report_event); + /* FIXME: Hack to simplify unit test, which is borderline. */ local->block_duration_rx_margin_ppm = UWB_BLOCK_DURATION_MARGIN_PPM; return &local->region; } @@ -58,7 +74,15 @@ static struct mcps802154_region *fira_open(struct mcps802154_llhw *llhw) static void fira_close(struct mcps802154_region *region) { struct fira_local *local = region_to_local(region); + struct fira_session *session, *s; + list_for_each_entry_safe (session, s, &local->inactive_sessions, + entry) { + fira_session_free(local, session); + } + + cancel_work_sync(&local->report_work); + skb_queue_purge(&local->report_queue); kfree_sensitive(local); } @@ -68,8 +92,8 @@ static void fira_notify_stop(struct mcps802154_region *region) struct fira_session *session, *s; list_for_each_entry_safe (session, s, &local->active_sessions, entry) { - session->stop_request = true; - fira_session_access_done(local, session, false); + fira_session_stop_controlees(session); + fira_session_fsm_stop(local, session); } } @@ -86,236 +110,300 @@ static int fira_call(struct mcps802154_region *region, u32 call_id, } } -static int fira_get_demand(struct mcps802154_region *region, - u32 next_timestamp_dtu, - struct mcps802154_region_demand *demand) +/** + * fira_session_init_block_start_dtu() - Build the first block start dtu. + * @local: FiRa context. + * @session: Session context. + * @timestamp_dtu: First access opportunity. + */ +static void fira_session_init_block_start_dtu(struct fira_local *local, + struct fira_session *session, + u32 timestamp_dtu) { - struct fira_local *local = region_to_local(region); - struct fira_session *session; - - session = fira_session_next( - local, next_timestamp_dtu + local->llhw->anticip_dtu, 0); - - if (session) { - fira_session_get_demand(local, session, demand); - demand->max_duration_dtu = session->last_access_duration_dtu; - local->current_session = session; - return 1; + if (!session->block_start_valid) { + const struct fira_session_params *params = &session->params; + int dtu_freq_khz = local->llhw->dtu_freq_hz / 1000; + + session->block_start_valid = true; + session->block_start_dtu = + timestamp_dtu + + params->initiation_time_ms * dtu_freq_khz; + session->next_access_timestamp_dtu = session->block_start_dtu; } - return 0; } -static int fira_report_local_aoa(struct sk_buff *msg, - const struct fira_local_aoa_info *info) -{ -#define A(x) FIRA_RANGING_DATA_MEASUREMENTS_AOA_ATTR_##x - if (nla_put_u8(msg, A(RX_ANTENNA_SET), info->rx_ant_set)) - goto nla_put_failure; - if (nla_put_s16(msg, A(AOA_2PI), info->aoa_2pi)) - goto nla_put_failure; - if (nla_put_s16(msg, A(PDOA_2PI), info->pdoa_2pi)) - goto nla_put_failure; - if (nla_put_u8(msg, A(AOA_FOM), info->aoa_fom)) - goto nla_put_failure; -#undef A - return 0; -nla_put_failure: - return -EMSGSIZE; -} - -static int fira_report_measurement(struct fira_local *local, - struct sk_buff *msg, - const struct fira_ranging_info *ranging_info) +/** + * fira_get_next_session() - Find the next session which should have the + * access. + * @local: FiRa context. + * @next_timestamp_dtu: Next access opportunity. + * @max_duration_dtu: Max duration of the next access opportunity. + * @adopted_demand: Output updated related to next session returned. + * + * Return: Pointer to the next session which should have the access. + */ +static struct fira_session * +fira_get_next_session(struct fira_local *local, u32 next_timestamp_dtu, + int max_duration_dtu, + struct fira_session_demand *adopted_demand) { - const struct fira_session *session = local->current_session; - struct nlattr *aoa; -#define A(x) FIRA_RANGING_DATA_MEASUREMENTS_ATTR_##x - - if (nla_put_u16(msg, A(SHORT_ADDR), ranging_info->short_addr) || - nla_put_u8(msg, A(STATUS), ranging_info->status)) - goto nla_put_failure; - - if (ranging_info->status) { - if (nla_put_u8(msg, A(SLOT_INDEX), ranging_info->slot_index)) - goto nla_put_failure; - return 0; - } + struct fira_session *adopted_session = NULL; + struct fira_session *session; + bool is_candidate_unsync, is_adopted_unsync; + int max_unsync_duration_dtu = max_duration_dtu; + int r; - if (ranging_info->tof_present) { - static const s64 speed_of_light_mm_per_s = 299702547000ull; - s32 distance_mm = div64_s64( - ranging_info->tof_rctu * speed_of_light_mm_per_s, - (s64)local->llhw->dtu_freq_hz * local->llhw->dtu_rctu); - if (nla_put_s32(msg, A(DISTANCE_MM), distance_mm)) - goto nla_put_failure; + /* + * Little cheat to start all sessions as initiation_time_ms is a + * delay, and not a absolute time. This is the only function + * which change the session content. + */ + list_for_each_entry (session, &local->active_sessions, entry) { + fira_session_init_block_start_dtu(local, session, + next_timestamp_dtu); } - if (ranging_info->local_aoa.present) { - aoa = nla_nest_start(msg, A(LOCAL_AOA)); - if (!aoa) - goto nla_put_failure; - if (fira_report_local_aoa(msg, &ranging_info->local_aoa)) - goto nla_put_failure; - nla_nest_end(msg, aoa); - } + /* + * Reminder: active_sessions list is sorted by session->priority + * from highest priority to lowest priority. + */ + list_for_each_entry (session, &local->active_sessions, entry) { + /* + * Welcome in sessions election! + * + * First, the candidate session will provide its wish in + * 'candidate_demand'. + * And then the candidate will be compared with the adopted + * session. The "best" will be become or stay the adopted + * session. + * So the session election will process candidate after + * candidate, to find the most appropriate session. + */ + struct fira_session_demand candidate_demand; + + /* + * Sessions with lower priority are not allowed to overlap + * the adopted session. But a lower priority can start and + * stop before the session adopted. + */ + if (adopted_session && adopted_session->params.priority != + session->params.priority) { + /* Is there some time left? */ + if (is_before_dtu(next_timestamp_dtu, + adopted_demand->timestamp_dtu)) + /* + * Limit max duration for session with lower + * priority to not overlap sessions which have + * an higher priority. + */ + max_duration_dtu = + adopted_demand->timestamp_dtu - + next_timestamp_dtu; + else + /* No more time left. */ + break; + if (!max_unsync_duration_dtu || + max_unsync_duration_dtu > max_duration_dtu) + max_unsync_duration_dtu = max_duration_dtu; + } - if (ranging_info->local_aoa_azimuth.present) { - aoa = nla_nest_start(msg, A(LOCAL_AOA_AZIMUTH)); - if (!aoa) - goto nla_put_failure; - if (fira_report_local_aoa(msg, - &ranging_info->local_aoa_azimuth)) - goto nla_put_failure; - nla_nest_end(msg, aoa); - } - if (ranging_info->local_aoa_elevation.present) { - aoa = nla_nest_start(msg, A(LOCAL_AOA_ELEVATION)); - if (!aoa) - goto nla_put_failure; - if (fira_report_local_aoa(msg, - &ranging_info->local_aoa_elevation)) - goto nla_put_failure; - nla_nest_end(msg, aoa); - } - if (ranging_info->remote_aoa_azimuth_present) { - if (nla_put_s16(msg, A(REMOTE_AOA_AZIMUTH_2PI), - ranging_info->remote_aoa_azimuth_2pi)) - goto nla_put_failure; - if (ranging_info->remote_aoa_fom_present) { - if (nla_put_u8(msg, A(REMOTE_AOA_AZIMUTH_FOM), - ranging_info->remote_aoa_azimuth_fom)) - goto nla_put_failure; + is_candidate_unsync = session->params.device_type == + FIRA_DEVICE_TYPE_CONTROLEE && + !session->controlee.synchronised; + /* Retrieve the wish of the session candidate. */ + r = fira_session_fsm_get_demand( + local, session, next_timestamp_dtu, + is_candidate_unsync ? max_unsync_duration_dtu : + max_duration_dtu, + &candidate_demand); + /* When 'r' is one, the session have a demand. */ + if (r != 1) + /* The session doesn't have a demand. */ + continue; + + /* + * If there is no adopted session, the candidate is the + * adopted session. + */ + if (!adopted_session) + goto candidate_adopted; + /* + * Is session finish before the adopted session ? + * adopted_demand | [-----] + * candidate | [------] + * --+-----------------------> Time + */ + if (is_before_dtu(candidate_demand.timestamp_dtu + + candidate_demand.max_duration_dtu, + adopted_demand->timestamp_dtu)) + /* + * Candidate is adopted and replace the + * previous one. + */ + goto candidate_adopted; + /* + * Is session start after the adopted session ? + * adopted_demand | [------] + * candidate | [--------] + * --+----------------------> Time + */ + if (is_before_dtu(adopted_demand->timestamp_dtu + + adopted_demand->max_duration_dtu, + candidate_demand.timestamp_dtu)) + /* Candidate is not adopted. */ + continue; + /* + * The candidate session have an overlap with the adopted + * session. Try the negotiation first to find an agreement + * about the access usage. + * + * But take care, synchronized session have a better + * eloquence in case of negotiation failure with an + * unsynchronized session. + */ + is_adopted_unsync = adopted_session->params.device_type == + FIRA_DEVICE_TYPE_CONTROLEE && + !adopted_session->controlee.synchronised; + /* + * The candidate session have an overlap with the adopted + * session. + * + * adopted_demand | [------] + * candidate | [--------] + * --+----------------------> Time + * + * Request a duration reduction to the adopted session. + */ + if (is_adopted_unsync && + !is_before_dtu(candidate_demand.timestamp_dtu, + adopted_demand->timestamp_dtu)) { + int limit_duration_dtu = + candidate_demand.timestamp_dtu - + adopted_demand->timestamp_dtu; + struct fira_session_demand tmp; + + if (limit_duration_dtu) + /* Ask to reduce the duration. */ + r = fira_session_fsm_get_demand( + local, adopted_session, + next_timestamp_dtu, limit_duration_dtu, + &tmp); + else + /* Both sessions start at same time. */ + r = 0; + if (r == 1) { + /* + * The adopted session accept to + * reduction its max duration. + */ + *adopted_demand = tmp; + max_unsync_duration_dtu = limit_duration_dtu; + continue; + } + if (!is_candidate_unsync) + /* + * In this corrupted world, synchronized + * session have better relation. + */ + goto candidate_adopted; } - } - if (ranging_info->remote_aoa_elevation_present) { - if (nla_put_s16(msg, A(REMOTE_AOA_ELEVATION_PI), - ranging_info->remote_aoa_elevation_pi)) - goto nla_put_failure; - if (ranging_info->remote_aoa_fom_present) { - if (nla_put_u8(msg, A(REMOTE_AOA_ELEVATION_FOM), - ranging_info->remote_aoa_elevation_fom)) - goto nla_put_failure; + /* + * The candidate session have an overlap with the adopted + * session. + * + * adopted_demand | [-----] + * candidate | [------] + * --+----------------------> Time + * + * Request a duration reduction to the candidate session. + */ + if (is_candidate_unsync && + !is_before_dtu(adopted_demand->timestamp_dtu, + candidate_demand.timestamp_dtu)) { + int limit_duration_dtu = adopted_demand->timestamp_dtu - + candidate_demand.timestamp_dtu; + struct fira_session_demand tmp; + + if (limit_duration_dtu) + /* Ask to reduce the duration. */ + r = fira_session_fsm_get_demand( + local, session, next_timestamp_dtu, + limit_duration_dtu, &tmp); + else + /* Both sessions start at same time. */ + r = 0; + if (r == 1) { + /* + * The candidate session accept to + * reduction its max duration. + */ + adopted_session = session; + *adopted_demand = tmp; + max_unsync_duration_dtu = limit_duration_dtu; + continue; + } + if (!is_adopted_unsync) + /* + * In this corrupted world, synchronized + * session have better relation. + */ + continue; } - } - if (ranging_info->data_payload_len > 0) { - if (nla_put(msg, A(DATA_PAYLOAD_RECV), - ranging_info->data_payload_len, - ranging_info->data_payload)) - goto nla_put_failure; - } - if (session->data_payload_seq_sent > 0) { - if (nla_put_u32(msg, A(DATA_PAYLOAD_SEQ_SENT), - session->data_payload_seq_sent)) - goto nla_put_failure; - } -#undef A - return 0; -nla_put_failure: - return -EMSGSIZE; + /* + * Finally, negotiation between adopted and candidate fails. + * One of the session will probably have ranging not done. + * Choose the session which have the oldest access. + */ + if (is_before_dtu(session->last_access_timestamp_dtu, + adopted_session->last_access_timestamp_dtu)) + goto candidate_adopted; + + /* Candidate is not adopted. */ + continue; + + candidate_adopted: + adopted_session = session; + *adopted_demand = candidate_demand; + } + return adopted_session; } -static int fira_report_measurement_stopped_controlee(struct fira_local *local, - struct sk_buff *msg, - __le16 short_addr) +static struct mcps802154_access * +fira_get_access(struct mcps802154_region *region, u32 next_timestamp_dtu, + int next_in_region_dtu, int region_duration_dtu) { -#define A(x) FIRA_RANGING_DATA_MEASUREMENTS_ATTR_##x - - if (nla_put_u16(msg, A(SHORT_ADDR), short_addr) || - nla_put_u8(msg, A(STOPPED), 1)) - goto nla_put_failure; - -#undef A - return 0; -nla_put_failure: - return -EMSGSIZE; + struct fira_local *local = region_to_local(region); + /* 'fsd' acronyms is FiRa Session Demand. */ + struct fira_session_demand fsd; + struct fira_session *session; + int max_duration_dtu = + region_duration_dtu ? region_duration_dtu - next_in_region_dtu : + 0; + + session = fira_get_next_session(local, next_timestamp_dtu, + max_duration_dtu, &fsd); + if (session) + return fira_session_fsm_get_access(local, session, &fsd); + return NULL; } -void fira_report(struct fira_local *local, struct fira_session *session, - bool add_measurements) +static int fira_get_demand(struct mcps802154_region *region, + u32 next_timestamp_dtu, + struct mcps802154_region_demand *next_demand) { - struct sk_buff *msg; - struct nlattr *data, *measurements, *measurement; - int ranging_interval_ms, i, r; - bool stop_completed; - - msg = mcps802154_region_event_alloc_skb(local->llhw, &local->region, - FIRA_CALL_SESSION_NOTIFICATION, - session->event_portid, - NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return; - - if (nla_put_u32(msg, FIRA_CALL_ATTR_SESSION_ID, session->id)) - goto nla_put_failure; - - data = nla_nest_start(msg, FIRA_CALL_ATTR_RANGING_DATA); - if (!data) - goto nla_put_failure; - - ranging_interval_ms = session->params.block_duration_dtu * - (session->block_stride_len + 1) / - (local->llhw->dtu_freq_hz / 1000); - if (nla_put_u32(msg, FIRA_RANGING_DATA_ATTR_BLOCK_INDEX, - session->block_index) || - nla_put_u32(msg, FIRA_RANGING_DATA_ATTR_RANGING_INTERVAL_MS, - ranging_interval_ms)) - goto nla_put_failure; - - stop_completed = (session->max_number_of_measurements_reached || - session->stop_request) && - !(session->controlee_management_flags & - FIRA_SESSION_CONTROLEE_MANAGEMENT_FLAG_STOP); - if (stop_completed || session->stop_inband || - session->stop_no_response) { - enum fira_ranging_data_attrs_stopped_values stopped_value = - stop_completed ? - FIRA_RANGING_DATA_ATTR_STOPPED_REQUEST : - session->stop_inband ? - FIRA_RANGING_DATA_ATTR_STOPPED_IN_BAND : - FIRA_RANGING_DATA_ATTR_STOPPED_NO_RESPONSE; - - if (nla_put_u8(msg, FIRA_RANGING_DATA_ATTR_STOPPED, - stopped_value)) - goto nla_put_failure; - } - - if (add_measurements && (local->n_ranging_info + - local->n_stopped_controlees_short_addr) != 0) { - measurements = nla_nest_start( - msg, FIRA_RANGING_DATA_ATTR_MEASUREMENTS); - if (!measurements) - goto nla_put_failure; - - for (i = 0; i < local->n_ranging_info; i++) { - measurement = nla_nest_start(msg, 1); - if (fira_report_measurement(local, msg, - &local->ranging_info[i])) - goto nla_put_failure; - nla_nest_end(msg, measurement); - } - - for (i = 0; i < local->n_stopped_controlees_short_addr; i++) { - measurement = nla_nest_start(msg, 1); - if (fira_report_measurement_stopped_controlee( - local, msg, - local->stopped_controlees_short_addr[i])) - goto nla_put_failure; - nla_nest_end(msg, measurement); - } + struct fira_local *local = region_to_local(region); + /* 'fsd' for FiRa Session Demand. */ + struct fira_session_demand fsd; + struct fira_session *session; - nla_nest_end(msg, measurements); + session = fira_get_next_session(local, next_timestamp_dtu, 0, &fsd); + if (session) { + next_demand->timestamp_dtu = fsd.timestamp_dtu; + next_demand->max_duration_dtu = fsd.max_duration_dtu; + return 1; } - - nla_nest_end(msg, data); - - r = mcps802154_region_event(local->llhw, msg); - if (r == -ECONNREFUSED) - /* TODO stop. */ - ; - return; -nla_put_failure: - kfree_skb(msg); + return 0; } static struct mcps802154_region_ops fira_region_ops = { @@ -329,6 +417,46 @@ static struct mcps802154_region_ops fira_region_ops = { .get_demand = fira_get_demand, }; +struct fira_session *fira_get_session_by_session_id(struct fira_local *local, + u32 session_id) +{ + struct fira_session *session; + + list_for_each_entry (session, &local->active_sessions, entry) { + if (session->id == session_id) + return session; + } + list_for_each_entry (session, &local->inactive_sessions, entry) { + if (session->id == session_id) + return session; + } + return NULL; +} + +void fira_check_all_missed_ranging(struct fira_local *local, + const struct fira_session *recent_session, + u32 timestamp_dtu) +{ + struct fira_session *session, *s; + + /* + * Process sessions with safe function, as the session FSM can leave + * the active list for many stop reasons. + */ + list_for_each_entry_safe (session, s, &local->active_sessions, entry) { + if (recent_session == session) + continue; + /* + * Does the session started during the access of + * recent_session? + */ + if (!session->block_start_valid) + continue; + fira_session_fsm_check_missed_ranging(local, session, + timestamp_dtu); + } +} + int __init fira_region_init(void) { int r; diff --git a/mac/fira_region.h b/mac/fira_region.h index 7feaf3f..8ec9d84 100644 --- a/mac/fira_region.h +++ b/mac/fira_region.h @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2020-2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. @@ -25,6 +25,7 @@ #define NET_FIRA_REGION_H #include <linux/kernel.h> +#include <linux/workqueue.h> #include <net/mcps802154_schedule.h> #include "net/fira_region_params.h" @@ -73,6 +74,41 @@ enum fira_message_id { }; /** + * struct fira_diagnostic - Diagnostic result. + */ +struct fira_diagnostic { + /** + * @rssis_q1: Received signal strength indication (RSSI), absolute value + * in Q1 fixed point format, unit is dBm. + */ + u8 rssis_q1[MCPS802154_RSSIS_N_MAX]; + /** + * @n_rssis: The number of RSSI in the array below. + */ + size_t n_rssis; + /** + * @aoas: Angle of arrival, ordered by increasing measurement type. + */ + struct mcps802154_rx_aoa_measurements + aoas[MCPS802154_RX_AOA_MEASUREMENTS_MAX]; + /** + * @n_aoas: Number of angle of arrival. + */ + size_t n_aoas; + /** + * @cirs: CIR for different parts of the frame. + * + * Set by low-level driver, must be kept valid until next received + * frame. + */ + struct mcps802154_rx_cir *cirs; + /** + * @n_cirs: Number of parts of CIR. + */ + size_t n_cirs; +}; + +/** * struct fira_slot - Information on an active slot. */ struct fira_slot { @@ -83,10 +119,9 @@ struct fira_slot { */ int index; /** - * @tx_controlee_index: Index of the controlee transmitting in this - * slot, or -1 for the controller. + * @controller_tx: True if Tx is performed by the controller. */ - int tx_controlee_index; + bool controller_tx; /** * @ranging_index: Index of the ranging in the ranging information * table, -1 if none. @@ -104,6 +139,10 @@ struct fira_slot { * @rx_ant_set: Rx antenna set. */ int rx_ant_set; + /** + * @controlee: Controlee. + */ + struct fira_controlee *controlee; }; /** @@ -173,6 +212,14 @@ struct fira_ranging_info { */ u8 remote_aoa_elevation_fom; /** + * @rx_rssis: RSSI value measured for individual Rx frames. + */ + u8 rx_rssis[FIRA_MESSAGE_ID_MAX + 1]; + /** + * @n_rx_rssis: Number of Rx RSSI saved. + */ + int n_rx_rssis; + /** * @short_addr: Peer short address. */ __le16 short_addr; @@ -216,6 +263,10 @@ struct fira_ranging_info { * @data_payload_len: Custom data payload length. */ int data_payload_len; + /** + * @rx_ctx: Pointer to the current rx_ctx context controlee. + */ + void *rx_ctx; }; /** @@ -235,6 +286,14 @@ struct fira_local { */ struct mcps802154_access access; /** + * @report_queue: Queue of report frame to be processed. + */ + struct sk_buff_head report_queue; + /** + * @report_work: Process work of report event. + */ + struct work_struct report_work; + /** * @frames: Access frames referenced from access. */ struct mcps802154_access_frame frames[FIRA_FRAMES_MAX]; @@ -247,10 +306,35 @@ struct fira_local { */ struct mcps802154_channel channel; /** + * @inactive_sessions: List of inactive sessions. + */ + struct list_head inactive_sessions; + /** + * @active_sessions: List of active sessions. + */ + struct list_head active_sessions; + /** * @current_session: Pointer to the current session. */ struct fira_session *current_session; /** + * @src_short_addr: Source address for the current session (actually + * never put as a source address in a frame, but used for control + * message). + */ + __le16 src_short_addr; + /** + * @dst_short_addr: Destination address for the current session. When + * controller, this is broadcast or the address of the only controlee. + * When controlee, this is the address of the controller. + */ + __le16 dst_short_addr; + /** + * @block_duration_rx_margin_ppm: Block duration rx margin for + * controlees. + */ + int block_duration_rx_margin_ppm; + /** * @slots: Descriptions of each active slots for the current session. * When controller, this is filled when the access is requested. When * controlee, the first slot is filled when the access is requested and @@ -275,41 +359,19 @@ struct fira_local { */ int n_ranging_valid; /** - * @src_short_addr: Source address for the current session (actually - * never put as a source address in a frame, but used for control - * message). - */ - __le16 src_short_addr; - /** - * @dst_short_addr: Destination address for the current session. When - * controller, this is broadcast or the address of the only controlee. - * When controlee, this is the address of the controller. - */ - __le16 dst_short_addr; - /** - * @inactive_sessions: List of inactive sessions. - */ - struct list_head inactive_sessions; - /** - * @active_sessions: List of active sessions. - */ - struct list_head active_sessions; - /** - * @stopped_controlees_short_addr: Short addresses of the stopped - * controlees for which an element must be added to the Device - * Management List of the RCM message. + * @diagnostics: Diagnostic collected for each slot. */ - __le16 stopped_controlees_short_addr[FIRA_CONTROLEES_MAX]; + struct fira_diagnostic diagnostics[FIRA_FRAMES_MAX]; /** - * @n_stopped_controlees_short_addr: Number of elements in the stopped - * controlees short addr table. + * @stopped_controlees: Short addresses of the stopped controlees for + * which an element must be added to the Device Management List of + * the control message. */ - int n_stopped_controlees_short_addr; + __le16 stopped_controlees[FIRA_CONTROLEES_MAX]; /** - * @block_duration_rx_margin_ppm: Block duration rx margin for - * controlees. + * @n_stopped_controlees: Number of elements in the stopped controlees . */ - int block_duration_rx_margin_ppm; + int n_stopped_controlees; }; static inline struct fira_local * @@ -325,12 +387,24 @@ access_to_local(struct mcps802154_access *access) } /** - * fira_report() - Report state change or ranging result for a session. + * fira_get_session_by_session_id() - Get a session by its identifier. + * @local: FiRa context. + * @session_id: Session identifier. + * + * Return: The session or NULL if not found. + */ +struct fira_session *fira_get_session_by_session_id(struct fira_local *local, + u32 session_id); + +/** + * fira_check_all_missed_ranging() - Check missed ranging round for all active + * session except the recent. * @local: FiRa context. - * @session: Session to report. Report ranging result if current session. - * @add_measurements: True to add measurements to report. + * @recent_session: FiRa session to not check in active list. + * @timestamp_dtu: Timestamp used to trig (or not) a report of ranging failure. */ -void fira_report(struct fira_local *local, struct fira_session *session, - bool add_measurements); +void fira_check_all_missed_ranging(struct fira_local *local, + const struct fira_session *recent_session, + u32 timestamp_dtu); #endif /* NET_FIRA_REGION_H */ diff --git a/mac/fira_region_call.c b/mac/fira_region_call.c index 2a66ab1..cb9f462 100644 --- a/mac/fira_region_call.c +++ b/mac/fira_region_call.c @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2020-2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. @@ -43,101 +43,61 @@ static const struct nla_policy fira_call_nla_policy[FIRA_CALL_ATTR_MAX + 1] = { static const struct nla_policy fira_session_param_nla_policy[FIRA_SESSION_PARAM_ATTR_MAX + 1] = { - [FIRA_SESSION_PARAM_ATTR_DEVICE_TYPE] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_DEVICE_TYPE_CONTROLLER, - }, - [FIRA_SESSION_PARAM_ATTR_DEVICE_ROLE] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_DEVICE_ROLE_INITIATOR, - }, - [FIRA_SESSION_PARAM_ATTR_RANGING_ROUND_USAGE] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_RANGING_ROUND_USAGE_DSTWR, - }, - [FIRA_SESSION_PARAM_ATTR_MULTI_NODE_MODE] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_MULTI_NODE_MODE_MANY_TO_MANY, - }, + [FIRA_SESSION_PARAM_ATTR_DEVICE_TYPE] = + NLA_POLICY_MAX(NLA_U8, FIRA_DEVICE_TYPE_CONTROLLER), + [FIRA_SESSION_PARAM_ATTR_DEVICE_ROLE] = + NLA_POLICY_MAX(NLA_U8, FIRA_DEVICE_ROLE_INITIATOR), + [FIRA_SESSION_PARAM_ATTR_RANGING_ROUND_USAGE] = + NLA_POLICY_MAX(NLA_U8, FIRA_RANGING_ROUND_USAGE_DSTWR), + [FIRA_SESSION_PARAM_ATTR_MULTI_NODE_MODE] = + NLA_POLICY_MAX(NLA_U8, FIRA_MULTI_NODE_MODE_MANY_TO_MANY), [FIRA_SESSION_PARAM_ATTR_SHORT_ADDR] = { .type = NLA_U16 }, [FIRA_SESSION_PARAM_ATTR_DESTINATION_SHORT_ADDR] = { .type = NLA_U16 }, [FIRA_SESSION_PARAM_ATTR_INITIATION_TIME_MS] = { .type = NLA_U32 }, [FIRA_SESSION_PARAM_ATTR_SLOT_DURATION_RSTU] = { .type = NLA_U32 }, [FIRA_SESSION_PARAM_ATTR_BLOCK_DURATION_MS] = { .type = NLA_U32 }, [FIRA_SESSION_PARAM_ATTR_ROUND_DURATION_SLOTS] = { .type = NLA_U32 }, - [FIRA_SESSION_PARAM_ATTR_BLOCK_STRIDE_LENGTH] = { - .type = NLA_U32, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_BLOCK_STRIDE_LEN_MAX, }, + [FIRA_SESSION_PARAM_ATTR_BLOCK_STRIDE_LENGTH] = + NLA_POLICY_MAX(NLA_U32, FIRA_BLOCK_STRIDE_LEN_MAX), [FIRA_SESSION_PARAM_ATTR_MAX_NUMBER_OF_MEASUREMENTS] = { .type = NLA_U32 }, [FIRA_SESSION_PARAM_ATTR_MAX_RR_RETRY] = { .type = NLA_U32 }, - [FIRA_SESSION_PARAM_ATTR_ROUND_HOPPING] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_BOOLEAN_MAX, - }, - [FIRA_SESSION_PARAM_ATTR_PRIORITY] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_PRIORITY_MAX, - }, - [FIRA_SESSION_PARAM_ATTR_RESULT_REPORT_PHASE] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_BOOLEAN_MAX, - }, - [FIRA_SESSION_PARAM_ATTR_MR_AT_INITIATOR] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_MEASUREMENT_REPORT_AT_INITIATOR, - }, - [FIRA_SESSION_PARAM_ATTR_EMBEDDED_MODE] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_EMBEDDED_MODE_NON_DEFERRED, - }, - [FIRA_SESSION_PARAM_ATTR_IN_BAND_TERMINATION_ATTEMPT_COUNT] = { - .type = NLA_U32, .validation_type = NLA_VALIDATE_RANGE, - .min = FIRA_IN_BAND_TERMINATION_ATTEMPT_COUNT_MIN, - .max = FIRA_IN_BAND_TERMINATION_ATTEMPT_COUNT_MAX, - }, + [FIRA_SESSION_PARAM_ATTR_ROUND_HOPPING] = + NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX), + [FIRA_SESSION_PARAM_ATTR_PRIORITY] = + NLA_POLICY_MAX(NLA_U8, FIRA_PRIORITY_MAX), + [FIRA_SESSION_PARAM_ATTR_RESULT_REPORT_PHASE] = + NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX), + [FIRA_SESSION_PARAM_ATTR_MR_AT_INITIATOR] = + NLA_POLICY_MAX(NLA_U8, FIRA_MEASUREMENT_REPORT_AT_INITIATOR), + [FIRA_SESSION_PARAM_ATTR_EMBEDDED_MODE] = + NLA_POLICY_MAX(NLA_U8, FIRA_EMBEDDED_MODE_NON_DEFERRED), + [FIRA_SESSION_PARAM_ATTR_IN_BAND_TERMINATION_ATTEMPT_COUNT] = + NLA_POLICY_RANGE(NLA_U32, + FIRA_IN_BAND_TERMINATION_ATTEMPT_COUNT_MIN, + FIRA_IN_BAND_TERMINATION_ATTEMPT_COUNT_MAX), [FIRA_SESSION_PARAM_ATTR_CHANNEL_NUMBER] = { .type = NLA_U8 }, [FIRA_SESSION_PARAM_ATTR_PREAMBLE_CODE_INDEX] = { .type = NLA_U8 }, - [FIRA_SESSION_PARAM_ATTR_RFRAME_CONFIG] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_RFRAME_CONFIG_SP3, - }, - [FIRA_SESSION_PARAM_ATTR_PRF_MODE] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_PRF_MODE_HPRF, - }, - [FIRA_SESSION_PARAM_ATTR_PREAMBLE_DURATION] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_PREAMBULE_DURATION_64, - }, - [FIRA_SESSION_PARAM_ATTR_SFD_ID] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_SFD_ID_4, - }, - [FIRA_SESSION_PARAM_ATTR_NUMBER_OF_STS_SEGMENTS] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_STS_SEGMENTS_2, - }, - [FIRA_SESSION_PARAM_ATTR_PSDU_DATA_RATE] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_PSDU_DATA_RATE_31M2, - }, - [FIRA_SESSION_PARAM_ATTR_BPRF_PHR_DATA_RATE] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_PHR_DATA_RATE_6M81, - }, - [FIRA_SESSION_PARAM_ATTR_MAC_FCS_TYPE] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_MAC_FCS_TYPE_CRC_32, - }, - [FIRA_SESSION_PARAM_ATTR_TX_ADAPTIVE_PAYLOAD_POWER] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_BOOLEAN_MAX, - }, + [FIRA_SESSION_PARAM_ATTR_RFRAME_CONFIG] = + NLA_POLICY_MAX(NLA_U8, FIRA_RFRAME_CONFIG_SP3), + [FIRA_SESSION_PARAM_ATTR_PRF_MODE] = + NLA_POLICY_MAX(NLA_U8, FIRA_PRF_MODE_HPRF_HIGH_RATE), + [FIRA_SESSION_PARAM_ATTR_PREAMBLE_DURATION] = + NLA_POLICY_MAX(NLA_U8, FIRA_PREAMBULE_DURATION_64), + [FIRA_SESSION_PARAM_ATTR_SFD_ID] = + NLA_POLICY_MAX(NLA_U8, FIRA_SFD_ID_4), + [FIRA_SESSION_PARAM_ATTR_NUMBER_OF_STS_SEGMENTS] = + NLA_POLICY_MAX(NLA_U8, FIRA_STS_SEGMENTS_4), + [FIRA_SESSION_PARAM_ATTR_PSDU_DATA_RATE] = + NLA_POLICY_MAX(NLA_U8, FIRA_PSDU_DATA_RATE_31M2), + [FIRA_SESSION_PARAM_ATTR_BPRF_PHR_DATA_RATE] = + NLA_POLICY_MAX(NLA_U8, FIRA_PHR_DATA_RATE_6M81), + [FIRA_SESSION_PARAM_ATTR_MAC_FCS_TYPE] = + NLA_POLICY_MAX(NLA_U8, FIRA_MAC_FCS_TYPE_CRC_32), + [FIRA_SESSION_PARAM_ATTR_TX_ADAPTIVE_PAYLOAD_POWER] = + NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX), [FIRA_SESSION_PARAM_ATTR_MEASUREMENT_SEQUENCE] = { .type = NLA_NESTED_ARRAY }, - [FIRA_SESSION_PARAM_ATTR_STS_CONFIG] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_STS_CONFIG_DYNAMIC_INDIVIDUAL_KEY, - }, + [FIRA_SESSION_PARAM_ATTR_STS_CONFIG] = + NLA_POLICY_MAX(NLA_U8, FIRA_STS_CONFIG_DYNAMIC_INDIVIDUAL_KEY), [FIRA_SESSION_PARAM_ATTR_SUB_SESSION_ID] = { .type = NLA_U32 }, [FIRA_SESSION_PARAM_ATTR_VUPPER64] = NLA_POLICY_EXACT_LEN(FIRA_VUPPER64_SIZE), @@ -145,82 +105,67 @@ static const struct nla_policy fira_session_param_nla_policy[FIRA_SESSION_PARAM_ .type = NLA_BINARY, .len = FIRA_KEY_SIZE_MAX, }, [FIRA_SESSION_PARAM_ATTR_SUB_SESSION_KEY] = { .type = NLA_BINARY, .len = FIRA_KEY_SIZE_MAX, }, - [FIRA_SESSION_PARAM_ATTR_KEY_ROTATION] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_BOOLEAN_MAX, }, + [FIRA_SESSION_PARAM_ATTR_KEY_ROTATION] = + NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX), [FIRA_SESSION_PARAM_ATTR_KEY_ROTATION_RATE] = { .type = NLA_U8 }, - [FIRA_SESSION_PARAM_ATTR_AOA_RESULT_REQ] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_BOOLEAN_MAX, - }, - [FIRA_SESSION_PARAM_ATTR_REPORT_TOF] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_BOOLEAN_MAX, - }, - [FIRA_SESSION_PARAM_ATTR_REPORT_AOA_AZIMUTH] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_BOOLEAN_MAX, - }, - [FIRA_SESSION_PARAM_ATTR_REPORT_AOA_ELEVATION] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_BOOLEAN_MAX, - }, - [FIRA_SESSION_PARAM_ATTR_REPORT_AOA_FOM] = { - .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = FIRA_BOOLEAN_MAX, - }, + [FIRA_SESSION_PARAM_ATTR_AOA_RESULT_REQ] = + NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX), + [FIRA_SESSION_PARAM_ATTR_REPORT_TOF] = + NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX), + [FIRA_SESSION_PARAM_ATTR_REPORT_AOA_AZIMUTH] = + NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX), + [FIRA_SESSION_PARAM_ATTR_REPORT_AOA_ELEVATION] = + NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX), + [FIRA_SESSION_PARAM_ATTR_REPORT_AOA_FOM] = + NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX), + [FIRA_SESSION_PARAM_ATTR_REPORT_RSSI] = + NLA_POLICY_MAX(NLA_U8, FIRA_RSSI_REPORT_AVERAGE), [FIRA_SESSION_PARAM_ATTR_DATA_VENDOR_OUI] = { .type = NLA_U32 }, [FIRA_SESSION_PARAM_ATTR_DATA_PAYLOAD] = { .type = NLA_BINARY, .len = FIRA_DATA_PAYLOAD_SIZE_MAX, }, + [FIRA_SESSION_PARAM_ATTR_DIAGNOSTICS] = + NLA_POLICY_MAX(NLA_U8, FIRA_BOOLEAN_MAX), + [FIRA_SESSION_PARAM_ATTR_DIAGNOSTICS_FRAME_REPORTS_FIELDS] = {.type = NLA_U32}, + [FIRA_SESSION_PARAM_ATTR_STS_LENGTH] = + NLA_POLICY_MAX(NLA_U8, FIRA_STS_LENGTH_128), + [FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_CONFIG] = + NLA_POLICY_MAX(NLA_U8, FIRA_RANGE_DATA_NTF_PROXIMITY), + [FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_PROXIMITY_NEAR] = + { .type = NLA_U32 }, + [FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_PROXIMITY_FAR] = + { .type = NLA_U32 }, }; /** - * get_session_state() - Get state of the session. + * fira_get_state_by_session_id() - Get state of the session. * @local: FiRa context. - * @session_id: Fira session id. + * @session_id: FiRa session id. * * Return: current session state. */ -static enum fira_session_state get_session_state(struct fira_local *local, - u32 session_id) +static enum fira_session_state_id +fira_get_state_by_session_id(struct fira_local *local, u32 session_id) { - struct fira_session *session; - bool active; - enum fira_session_state state; - - /* Determine if session exists already. */ - session = fira_session_get(local, session_id, &active); - if (!session) { - state = FIRA_SESSION_STATE_DEINIT; - } else { - /* Is session active? */ - if (active) { - state = FIRA_SESSION_STATE_ACTIVE; - } else { - /* Is session ready? */ - if (fira_session_is_ready(local, session)) - state = FIRA_SESSION_STATE_IDLE; - else - state = FIRA_SESSION_STATE_INIT; - } - } + struct fira_session *session = + fira_get_session_by_session_id(local, session_id); - return state; + if (session) + return fira_session_get_state_id(session); + return FIRA_SESSION_STATE_ID_DEINIT; } /** - * fira_session_init() - Initialize Fira session. + * fira_session_init() - Initialize FiRa session. * @local: FiRa context. - * @session_id: Fira session id. + * @session_id: FiRa session id. * * Return: 0 or error. */ static int fira_session_init(struct fira_local *local, u32 session_id) { - bool active; - struct fira_session *session; + struct fira_session *session = + fira_get_session_by_session_id(local, session_id); - session = fira_session_get(local, session_id, &active); if (session) return -EBUSY; @@ -232,9 +177,9 @@ static int fira_session_init(struct fira_local *local, u32 session_id) } /** - * fira_session_start() - Start Fira session. + * fira_session_start() - Start FiRa session. * @local: FiRa context. - * @session_id: Fira session id. + * @session_id: FiRa session id. * @info: Request information. * * Return: 0 or error. @@ -242,191 +187,51 @@ static int fira_session_init(struct fira_local *local, u32 session_id) static int fira_session_start(struct fira_local *local, u32 session_id, const struct genl_info *info) { - int n; - bool active; struct fira_session *session; - session = fira_session_get(local, session_id, &active); + session = fira_get_session_by_session_id(local, session_id); if (!session) return -ENOENT; - trace_region_fira_session_params(session, &session->params); - for (n = 0; n < session->params.meas_seq.active->n_steps; n++) - trace_region_fira_meas_seq_step( - session, &(session->params.meas_seq.active->steps[n]), - n); - - if (!fira_session_is_ready(local, session)) - return -EINVAL; - - session->event_portid = info->snd_portid; - - if (!active) { - u32 now_dtu; - int initiation_time_dtu; - int block_stride_len; - int r; - - r = fira_crypto_derive_per_session(local, session); - if (r) - return r; - r = fira_crypto_derive_per_rotation(local, session, 0); - if (r) - return r; - - r = mcps802154_get_current_timestamp_dtu(local->llhw, &now_dtu); - if (r) - return r; - - if (session->params.initiation_time_ms) { - initiation_time_dtu = - session->params.initiation_time_ms * - (local->llhw->dtu_freq_hz / 1000); - } else if (session->params.device_type == - FIRA_DEVICE_TYPE_CONTROLLER && - local->llhw->anticip_dtu) { - /* In order to be able to generate a frame for - sts_index = sts_index_init, add anticip_dtu two - times, for mcps802154_fproc_access_now and for - fira_get_access. - Also add 5 ms delay since now_dtu will change between - fira_session_start and fira_get_access. */ - initiation_time_dtu = - 2 * local->llhw->anticip_dtu + - 5 * (local->llhw->dtu_freq_hz / 1000); - } else { - initiation_time_dtu = 0; - } - - if (session->params.device_type == FIRA_DEVICE_TYPE_CONTROLLER) - block_stride_len = session->params.block_stride_len; - else - block_stride_len = 0; - - session->block_start_dtu = now_dtu + initiation_time_dtu; - session->block_index = 0; - session->sts_index = session->crypto.sts_index_init; - session->hopping_sequence_generation = - session->params.round_hopping; - session->round_index = 0; - session->next_round_index = 0; - session->block_stride_len = block_stride_len; - session->next_block_stride_len = block_stride_len; - session->synchronised = false; - session->last_access_timestamp_dtu = -1; - session->last_access_duration_dtu = 0; - session->last_block_index = -(block_stride_len + 1); - fira_session_update_round_index(session); - session->number_of_measurements = 0; - - session->params.meas_seq.current_step = 0; - session->params.meas_seq.n_measurements_achieved = 0; - - list_move(&session->entry, &local->active_sessions); - - mcps802154_reschedule(local->llhw); - } - - return 0; + return fira_session_fsm_start(local, session, info); } /** - * fira_session_stop() - Stop Fira session. + * fira_session_stop() - Stop FiRa session. * @local: FiRa context. - * @session_id: Fira session id. + * @session_id: FiRa session id. * * Return: 0 or error. */ static int fira_session_stop(struct fira_local *local, u32 session_id) { - bool active; struct fira_session *session; - session = fira_session_get(local, session_id, &active); + session = fira_get_session_by_session_id(local, session_id); if (!session) return -ENOENT; - if (active) { - session->stop_request = true; - if (session->params.device_type == FIRA_DEVICE_TYPE_CONTROLEE) { - if (local->current_session != session) - fira_session_access_done(local, session, false); - else - mcps802154_reschedule(local->llhw); - } else { - if (local->current_session == NULL || - (local->current_session != session && - !session->current_controlees.size)) { - fira_session_access_done(local, session, false); - } else { - if (session->controlee_management_flags) { - /* Many changes on same round or while a controlee is stopping is not supported. */ - return -EBUSY; - } - fira_session_copy_controlees( - &session->new_controlees, - &session->current_controlees); - fira_session_stop_controlees( - session, &session->new_controlees); - session->controlee_management_flags = - FIRA_SESSION_CONTROLEE_MANAGEMENT_FLAG_UPDATE | - FIRA_SESSION_CONTROLEE_MANAGEMENT_FLAG_STOP; - } - } - } - - return 0; + return fira_session_fsm_stop(local, session); } /** - * fira_session_deinit() - Deinitialize Fira session. + * fira_session_deinit() - Deinitialize FiRa session. * @local: FiRa context. - * @session_id: Fira session id. + * @session_id: FiRa session id. * * Return: 0 or error. */ static int fira_session_deinit(struct fira_local *local, u32 session_id) { - bool active; - struct fira_session *session; + struct fira_session *session = + fira_get_session_by_session_id(local, session_id); - session = fira_session_get(local, session_id, &active); if (!session) return -ENOENT; - if (active) + if (fira_session_is_active(session)) return -EBUSY; - fira_session_free(session); - return 0; -} - -static int is_allowed_param_active(enum fira_device_type dev_type, int param) -{ - /* These arrays contain attributes which can be changed in an active - controller or controlee session */ - static const int allowed_controller_param_active[] = { - FIRA_SESSION_PARAM_ATTR_DATA_PAYLOAD, - FIRA_SESSION_PARAM_ATTR_BLOCK_STRIDE_LENGTH, - FIRA_SESSION_PARAM_ATTR_MEASUREMENT_SEQUENCE, - }; - static const int allowed_controlee_param_active[] = { - FIRA_SESSION_PARAM_ATTR_DATA_PAYLOAD, - FIRA_SESSION_PARAM_ATTR_MEASUREMENT_SEQUENCE, - }; - const int *allowed_param_active = - dev_type == FIRA_DEVICE_TYPE_CONTROLLER ? - allowed_controller_param_active : - allowed_controlee_param_active; - const int num_allowed = - dev_type == FIRA_DEVICE_TYPE_CONTROLLER ? - ARRAY_SIZE(allowed_controller_param_active) : - ARRAY_SIZE(allowed_controlee_param_active); - int i; - - for (i = 0; i < num_allowed; i++) - if (allowed_param_active[i] == param) - return 1; - + fira_session_free(local, session); return 0; } @@ -456,13 +261,11 @@ static int fira_session_params_set_measurement_sequence_step( [STEP_ATTR(TX_ANT_SET_NONRANGING)] = { .type = NLA_U8 }, [STEP_ATTR(TX_ANT_SET_RANGING)] = { .type = NLA_U8 }, }; - static const struct nla_policy rx_ant_sets_ranging_policy[ASR_ATTR(MAX) + 1] = { [ASR_ATTR(0)] = { .type = NLA_U8 }, [ASR_ATTR(1)] = { .type = NLA_U8 }, }; - struct nlattr *step_attrs[STEP_ATTR(MAX) + 1]; struct nlattr *rx_ant_sets_attrs[ASR_ATTR(MAX) + 1]; int r = 0; @@ -473,12 +276,11 @@ static int fira_session_params_set_measurement_sequence_step( r = nla_parse_nested(step_attrs, STEP_ATTR(MAX), params, meas_seq_step_policy, info->extack); /* LCOV_EXCL_START */ - /* defensive check, should not happen */ if (r) return r; /* LCOV_EXCL_STOP */ - memset((void *)step, 0, sizeof(struct fira_measurement_sequence_step)); + memset(step, 0, sizeof(struct fira_measurement_sequence_step)); if (!step_attrs[STEP_ATTR(MEASUREMENT_TYPE)] || !step_attrs[STEP_ATTR(N_MEASUREMENTS)]) return -EINVAL; @@ -507,7 +309,6 @@ static int fira_session_params_set_measurement_sequence_step( step_attrs[STEP_ATTR(RX_ANT_SETS_RANGING)], rx_ant_sets_ranging_policy, info->extack); /* LCOV_EXCL_START */ - /* defensive check, should not happen */ if (r) return r; /* LCOV_EXCL_STOP */ @@ -548,41 +349,58 @@ static int fira_session_params_set_measurement_sequence( const struct nlattr *params, const struct genl_info *info, struct fira_measurement_sequence *meas_seq) { - struct nlattr *request = NULL; + struct nlattr *request; int r, rem = 0; size_t n_steps = 0; - /* LCOV_EXCL_START */ - /* defensive check, not easy to trigger using unit test */ - if (!params) - return -EINVAL; - if (!meas_seq) - return -EINVAL; - /* LCOV_EXCL_STOP */ - nla_for_each_nested (request, params, rem) { if (n_steps >= FIRA_MEASUREMENT_SEQUENCE_STEP_MAX) return -EINVAL; - r = fira_session_params_set_measurement_sequence_step( - request, info, meas_seq->steps + n_steps); + request, info, &meas_seq->steps[n_steps]); if (r) return r; - - ++n_steps; + n_steps++; } - - if (0 == n_steps) + if (!n_steps) return -EINVAL; meas_seq->n_steps = n_steps; + return 0; +} +/** + * check_parameter_proximity_range() - Check proximity range concistency. + * @params: Current session parameters. + * @set_attrs: Updated session parameters. + * + * Return: 0 or error. + */ +static inline int +check_parameter_proximity_range(const struct fira_session_params *params, + struct nlattr *const *set_attrs) +{ + uint32_t proximity_near = params->range_data_ntf_proximity_near_mm; + uint32_t proximity_far = params->range_data_ntf_proximity_far_mm; + const struct nlattr *near_attr = + set_attrs[FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_PROXIMITY_NEAR]; + const struct nlattr *far_attr = + set_attrs[FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_PROXIMITY_FAR]; + if (near_attr) { + proximity_near = nla_get_u32(near_attr); + } + if (far_attr) { + proximity_far = nla_get_u32(far_attr); + } + if (proximity_near > proximity_far) { + return -ERANGE; + } return 0; } /** - * fira_session_set_parameters() - Set Fira session parameters. + * fira_session_set_parameters() - Set FiRa session parameters. * @local: FiRa context. - * @session_id: Fira session id. + * @session_id: FiRa session id. * @params: Nested attribute containing session parameters. * @info: Request information. * @@ -593,38 +411,38 @@ static int fira_session_set_parameters(struct fira_local *local, u32 session_id, const struct genl_info *info) { struct nlattr *attrs[FIRA_SESSION_PARAM_ATTR_MAX + 1]; - bool active; struct fira_session *session; struct fira_session_params *p; - struct fira_measurement_sequence *meas_seq; + struct fira_measurement_sequence meas_seq = {}; int r; - session = fira_session_get(local, session_id, &active); - if (!session) - return -ENOENT; if (!params) return -EINVAL; + session = fira_get_session_by_session_id(local, session_id); + if (!session) + return -ENOENT; + r = nla_parse_nested(attrs, FIRA_SESSION_PARAM_ATTR_MAX, params, fira_session_param_nla_policy, info->extack); if (r) return r; + /* Check attribute validity. */ + r = check_parameter_proximity_range(&session->params, attrs); + if (r) + return r; + if (attrs[FIRA_SESSION_PARAM_ATTR_MEASUREMENT_SEQUENCE]) { + r = fira_session_params_set_measurement_sequence( + attrs[FIRA_SESSION_PARAM_ATTR_MEASUREMENT_SEQUENCE], + info, &meas_seq); + if (r) + return r; + } + r = fira_session_fsm_check_parameters(session, attrs); + if (r) + return r; p = &session->params; - if (p->meas_seq.active == &(p->_meas_seq_1)) - meas_seq = &(p->_meas_seq_2); - else - meas_seq = &(p->_meas_seq_1); - - if (active) { - int i; - for (i = FIRA_SESSION_PARAM_ATTR_UNSPEC + 1; - i <= FIRA_SESSION_PARAM_ATTR_MAX; i++) { - if (attrs[i] && - !is_allowed_param_active(p->device_type, i)) - return -EBUSY; - } - } #define P(attr, member, type, conv) \ do { \ int x; \ @@ -678,21 +496,15 @@ static int fira_session_set_parameters(struct fira_local *local, u32 session_id, P(RFRAME_CONFIG, rframe_config, u8, x); P(PREAMBLE_DURATION, preamble_duration, u8, x); P(SFD_ID, sfd_id, u8, x); + P(NUMBER_OF_STS_SEGMENTS, number_of_sts_segments, u8, x); P(PSDU_DATA_RATE, psdu_data_rate, u8, x); P(MAC_FCS_TYPE, mac_fcs_type, u8, x); + P(PRF_MODE, prf_mode, u8, x); + P(BPRF_PHR_DATA_RATE, phr_data_rate, u8, x); /* Measurement Sequence */ if (attrs[FIRA_SESSION_PARAM_ATTR_MEASUREMENT_SEQUENCE]) { - if (fira_session_params_set_measurement_sequence( - attrs[FIRA_SESSION_PARAM_ATTR_MEASUREMENT_SEQUENCE], - info, meas_seq)) { - return -EINVAL; - } else if (!active) { - /* immediate update */ - p->meas_seq.active = meas_seq; - } else { - /* deferred update when session is running */ - p->meas_seq.update_flag = true; - } + p->meas_seq = meas_seq; + session->measurements.reset = true; } /* STS and crypto parameters. */ PMEMCPY(VUPPER64, vupper64); @@ -702,35 +514,45 @@ static int fira_session_set_parameters(struct fira_local *local, u32 session_id, P(REPORT_AOA_AZIMUTH, report_aoa_azimuth, u8, !!x); P(REPORT_AOA_ELEVATION, report_aoa_elevation, u8, !!x); P(REPORT_AOA_FOM, report_aoa_fom, u8, !!x); + P(REPORT_RSSI, report_rssi, u8, x); /* Custom data */ P(DATA_VENDOR_OUI, data_vendor_oui, u32, x); PMEMNCPY(DATA_PAYLOAD, data_payload, data_payload_len); /* Increment sequence number if a new data is received. */ - if (attrs[FIRA_SESSION_PARAM_ATTR_DATA_PAYLOAD]) { + if (attrs[FIRA_SESSION_PARAM_ATTR_DATA_PAYLOAD]) p->data_payload_seq++; - } - /* TODO: set all fira session parameters. */ -#undef P -#undef PMEMCPY + /* Diagnostics */ + P(DIAGNOSTICS, report_diagnostics, u8, x); + P(DIAGNOSTICS_FRAME_REPORTS_FIELDS, diagnostic_report_flags, u32, x); + /* Misc */ + P(STS_LENGTH, sts_length, u8, x); + P(RANGE_DATA_NTF_CONFIG, range_data_ntf_config, u8, x); + P(RANGE_DATA_NTF_PROXIMITY_NEAR, range_data_ntf_proximity_near_mm, u32, + x); + P(RANGE_DATA_NTF_PROXIMITY_FAR, range_data_ntf_proximity_far_mm, u32, + x); #undef PMEMNCPY +#undef PMEMCPY +#undef P + fira_session_fsm_parameters_updated(local, session); return 0; } /** * fira_session_get_state() - Get state of the session. * @local: FiRa context. - * @session_id: Fira session id. + * @session_id: FiRa session id. * * Return: 0 or error. */ static int fira_session_get_state(struct fira_local *local, u32 session_id) { struct sk_buff *msg; - enum fira_session_state state; + enum fira_session_state_id state; - state = get_session_state(local, session_id); + state = fira_get_state_by_session_id(local, session_id); msg = mcps802154_region_call_alloc_reply_skb( local->llhw, &local->region, FIRA_CALL_SESSION_GET_STATE, @@ -821,12 +643,6 @@ static int fira_session_params_get_measurement_sequence( struct nlattr *meas_seq_params = NULL; size_t i; - /* LCOV_EXCL_START */ - /* defensive check, should not happen */ - if (!meas_seq || !msg_buf) - return -EINVAL; - /* LCOV_EXCL_STOP */ - meas_seq_params = nla_nest_start( msg_buf, FIRA_SESSION_PARAM_ATTR_MEASUREMENT_SEQUENCE); if (!meas_seq_params) @@ -846,26 +662,24 @@ static int fira_session_params_get_measurement_sequence( } /** - * fira_session_get_parameters() - Get Fira session parameters. + * fira_session_get_parameters() - Get FiRa session parameters. * @local: FiRa context. - * @session_id: Fira session id. + * @session_id: FiRa session id. * * Return: 0 or error. */ static int fira_session_get_parameters(struct fira_local *local, u32 session_id) { - bool active; const struct fira_session *session; const struct fira_session_params *p; struct sk_buff *msg; struct nlattr *params; - session = fira_session_get(local, session_id, &active); + session = fira_get_session_by_session_id(local, session_id); if (!session) return -ENOENT; p = &session->params; - msg = mcps802154_region_call_alloc_reply_skb( local->llhw, &local->region, FIRA_CALL_SESSION_GET_PARAMS, NLMSG_DEFAULT_SIZE); @@ -921,13 +735,15 @@ static int fira_session_get_parameters(struct fira_local *local, u32 session_id) P(RFRAME_CONFIG, rframe_config, u8, x); P(PREAMBLE_DURATION, preamble_duration, u8, x); P(SFD_ID, sfd_id, u8, x); + P(NUMBER_OF_STS_SEGMENTS, number_of_sts_segments, u8, x); P(PSDU_DATA_RATE, psdu_data_rate, u8, x); + P(PRF_MODE, prf_mode, u8, x); + P(BPRF_PHR_DATA_RATE, phr_data_rate, u8, x); P(MAC_FCS_TYPE, mac_fcs_type, u8, x); /* Measurement Sequence */ if (fira_session_params_get_measurement_sequence( - session->params.meas_seq.active, msg)) + &session->measurements.sequence, msg)) goto nla_put_failure; - /* STS and crypto parameters. */ PMEMCPY(VUPPER64, vupper64); /* Report parameters. */ @@ -936,9 +752,20 @@ static int fira_session_get_parameters(struct fira_local *local, u32 session_id) P(REPORT_AOA_AZIMUTH, report_aoa_azimuth, u8, !!x); P(REPORT_AOA_ELEVATION, report_aoa_elevation, u8, !!x); P(REPORT_AOA_FOM, report_aoa_fom, u8, !!x); + P(REPORT_RSSI, report_rssi, u8, !!x); /* Custom data */ if (p->data_vendor_oui) P(DATA_VENDOR_OUI, data_vendor_oui, u32, x); + /* Diagnostics */ + P(DIAGNOSTICS, report_diagnostics, u8, x); + P(DIAGNOSTICS_FRAME_REPORTS_FIELDS, diagnostic_report_flags, u32, x); + /* Misc */ + P(STS_LENGTH, sts_length, u8, x); + P(RANGE_DATA_NTF_CONFIG, range_data_ntf_config, u8, x); + P(RANGE_DATA_NTF_PROXIMITY_NEAR, range_data_ntf_proximity_near_mm, u32, + x); + P(RANGE_DATA_NTF_PROXIMITY_FAR, range_data_ntf_proximity_far_mm, u32, + x); #undef P #undef PMEMCPY @@ -953,14 +780,15 @@ nla_put_failure: /** * fira_manage_controlees() - Manage controlees. * @local: FiRa context. - * @call_id: Fira call id. - * @session_id: Fira session id. + * @call_id: FiRa call id. + * @session_id: FiRa session id. * @params: Nested attribute containing controlee parameters. * @info: Request information. * Return: 0 or error. */ -static int fira_manage_controlees(struct fira_local *local, u32 call_id, - u32 session_id, const struct nlattr *params, +static int fira_manage_controlees(struct fira_local *local, + enum fira_call call_id, u32 session_id, + const struct nlattr *params, const struct genl_info *info) { static const struct nla_policy new_controlee_nla_policy[FIRA_CALL_CONTROLEE_ATTR_MAX + @@ -971,121 +799,148 @@ static int fira_manage_controlees(struct fira_local *local, u32 call_id, .len = FIRA_KEY_SIZE_MAX }, }; struct nlattr *request; - struct fira_controlee controlees[FIRA_CONTROLEES_MAX]; struct nlattr *attrs[FIRA_CALL_CONTROLEE_ATTR_MAX + 1]; int r, rem, i, n_controlees = 0; struct fira_session *session; - struct fira_controlees_array *controlees_array; - bool active; + struct fira_controlee *controlee, *tmp_controlee; + bool is_active; + struct list_head controlees; if (!params) return -EINVAL; + session = fira_get_session_by_session_id(local, session_id); + if (!session) + return -ENOENT; + + INIT_LIST_HEAD(&controlees); + nla_for_each_nested (request, params, rem) { - if (n_controlees >= FIRA_CONTROLEES_MAX) - return -EINVAL; + if (n_controlees >= FIRA_CONTROLEES_MAX) { + r = -EINVAL; + goto end; + } r = nla_parse_nested(attrs, FIRA_CALL_CONTROLEE_ATTR_MAX, request, new_controlee_nla_policy, info->extack); if (r) - return r; + goto end; + + controlee = kzalloc(sizeof(struct fira_controlee), GFP_KERNEL); + if (!controlee) { + r = -ENOMEM; + goto end; + } if (!attrs[FIRA_CALL_CONTROLEE_ATTR_SHORT_ADDR] || (!attrs[FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_ID] ^ - !attrs[FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_KEY])) - return -EINVAL; + !attrs[FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_KEY])) { + kfree(controlee); + r = -EINVAL; + goto end; + } - controlees[n_controlees].short_addr = nla_get_le16( + controlee->short_addr = nla_get_le16( attrs[FIRA_CALL_CONTROLEE_ATTR_SHORT_ADDR]); if (attrs[FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_ID]) { - if (call_id == FIRA_CALL_DEL_CONTROLEE) - return -EINVAL; - controlees[n_controlees].sub_session = true; - controlees[n_controlees].sub_session_id = nla_get_u32( + if (call_id == FIRA_CALL_DEL_CONTROLEE) { + kfree(controlee); + r = -EINVAL; + goto end; + } + controlee->sub_session = true; + controlee->sub_session_id = nla_get_u32( attrs[FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_ID]); - memcpy(controlees[n_controlees].sub_session_key, + memcpy(controlee->sub_session_key, nla_data( attrs[FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_KEY]), nla_len(attrs[FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_KEY])); - controlees[n_controlees].sub_session_key_len = nla_len( + controlee->sub_session_key_len = nla_len( attrs[FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_KEY]); - } else - controlees[n_controlees].sub_session = false; - controlees[n_controlees].state = FIRA_CONTROLEE_STATE_RUNNING; - - for (i = 0; i < n_controlees; i++) { - if (controlees[n_controlees].short_addr == - controlees[i].short_addr) - return -EINVAL; + } else { + controlee->sub_session = false; } - + controlee->state = FIRA_CONTROLEE_STATE_RUNNING; + /* Check and reject a duplication of short_addr. */ + list_for_each_entry (tmp_controlee, &controlees, entry) { + if (controlee->short_addr == + tmp_controlee->short_addr) { + kfree(controlee); + r = -EINVAL; + goto end; + } + } + list_add_tail(&controlee->entry, &controlees); n_controlees++; } - if (!n_controlees) - return -EINVAL; + if (list_empty(&controlees)) { + r = -EINVAL; + goto end; + } - session = fira_session_get(local, session_id, &active); - if (!session) - return -ENOENT; + /* + * Be careful, active is not equal to + * 'local->current_session == session'. + */ + is_active = fira_session_is_active(session); - if (session->controlee_management_flags) { - /* Many changes on same round or while a controlee is stopping is not supported. */ - return -EBUSY; - } else if (active) { + if (is_active) { /* If unicast refuse to add more than one controlee. */ - if (call_id == FIRA_CALL_NEW_CONTROLEE && - session->params.multi_node_mode == - FIRA_MULTI_NODE_MODE_UNICAST && - (session->current_controlees.size > 0 || n_controlees > 1)) - return -EINVAL; - if (call_id == FIRA_CALL_SET_CONTROLEE) - return -EBUSY; - fira_session_copy_controlees(&session->new_controlees, - &session->current_controlees); - /* Use second array to not disturbe active session. */ - controlees_array = &session->new_controlees; - } else { - /* No risk to disturbe this session. */ - controlees_array = &session->current_controlees; + switch (call_id) { + case FIRA_CALL_NEW_CONTROLEE: + if (session->params.multi_node_mode == + FIRA_MULTI_NODE_MODE_UNICAST && + (!list_empty(&session->current_controlees) || + n_controlees > 1)) { + r = -EINVAL; + goto end; + } + break; + case FIRA_CALL_SET_CONTROLEE: + r = -EBUSY; + goto end; + default: + break; + } } switch (call_id) { case FIRA_CALL_SET_CONTROLEE: - r = fira_session_set_controlees(local, session, - controlees_array, controlees, + r = fira_session_set_controlees(local, session, &controlees, n_controlees); break; case FIRA_CALL_DEL_CONTROLEE: - if (active) - r = fira_session_async_del_controlees(session, - controlees_array, - controlees, - n_controlees); - else - r = fira_session_del_controlees( - controlees_array, controlees, n_controlees); + r = fira_session_del_controlees(session, &controlees, + is_active); break; /* FIRA_CALL_NEW_CONTROLEE. */ default: - r = fira_session_new_controlees(session, active, - controlees_array, controlees, - n_controlees); + r = fira_session_new_controlees(session, &controlees, + n_controlees, is_active); } if (r) - return r; + goto end; - if (active) - session->controlee_management_flags |= - FIRA_SESSION_CONTROLEE_MANAGEMENT_FLAG_UPDATE; + if (!is_active && local->llhw->rx_ctx_size) { + for (i = 0; i < session->n_current_controlees; i++) { + memset(session->rx_ctx[i], 0, local->llhw->rx_ctx_size); + } + } - return 0; + fira_session_fsm_controlee_list_updated(local, session); +end: + list_for_each_entry_safe (controlee, tmp_controlee, &controlees, + entry) { + kfree(controlee); + } + return r; } /** * fira_session_get_controlees() - Get list of controlees. * @local: FiRa context. - * @session_id: Fira session id. + * @session_id: FiRa session id. * @info: Request information. * * Return: 0 or error. @@ -1093,13 +948,12 @@ static int fira_manage_controlees(struct fira_local *local, u32 call_id, static int fira_session_get_controlees(struct fira_local *local, u32 session_id, const struct genl_info *info) { - bool active; const struct fira_session *session; struct sk_buff *msg; - struct nlattr *controlees, *controlee; - int i; + struct nlattr *controlees, *controlee_attr; + struct fira_controlee *controlee; - session = fira_session_get(local, session_id, &active); + session = fira_get_session_by_session_id(local, session_id); if (!session) return -ENOENT; @@ -1117,20 +971,18 @@ static int fira_session_get_controlees(struct fira_local *local, u32 session_id, if (!controlees) goto nla_put_failure; - for (i = 0; i < session->current_controlees.size; i++) { - const struct fira_controlee *c = - &session->current_controlees.data[i]; - controlee = nla_nest_start(msg, 1); - if (!controlee) + list_for_each_entry (controlee, &session->current_controlees, entry) { + controlee_attr = nla_nest_start(msg, 1); + if (!controlee_attr) goto nla_put_failure; if (nla_put_le16(msg, FIRA_CALL_CONTROLEE_ATTR_SHORT_ADDR, - c->short_addr)) + controlee->short_addr)) goto nla_put_failure; - if (c->sub_session && + if (controlee->sub_session && nla_put_u32(msg, FIRA_CALL_CONTROLEE_ATTR_SUB_SESSION_ID, - c->sub_session_id)) + controlee->sub_session_id)) goto nla_put_failure; - nla_nest_end(msg, controlee); + nla_nest_end(msg, controlee_attr); } nla_nest_end(msg, controlees); @@ -1146,6 +998,7 @@ int fira_get_capabilities(struct fira_local *local, { struct sk_buff *msg; struct nlattr *capabilities; + u64 hw_flags = local->llhw->flags; if (!info) return 0; @@ -1174,6 +1027,11 @@ int fira_get_capabilities(struct fira_local *local, goto nla_put_failure; \ } \ } while (0) +#define C(name, hw_flag) \ + do { \ + if (hw_flags & (hw_flag)) \ + F(name); \ + } while (0) /* Main session capabilities. */ P(FIRA_PHY_VERSION_RANGE, u32, 0x01010101); @@ -1188,29 +1046,46 @@ int fira_get_capabilities(struct fira_local *local, /* Behaviour. */ F(ROUND_HOPPING); F(BLOCK_STRIDING); + /* STS and crypto capabilities. */ + F(STS_CONFIG_STATIC); /* Radio. */ P(CHANNEL_NUMBER, u16, local->llhw->hw->phy->supported .channels[local->llhw->hw->phy->current_page]); - F(RFRAME_CONFIG_SP1); - F(RFRAME_CONFIG_SP3); - F(PRF_MODE_BPRF); - F(PREAMBLE_DURATION_64); - F(SFD_ID_2); + C(RFRAME_CONFIG_SP1, + MCPS802154_LLHW_STS_SEGMENT_1 | MCPS802154_LLHW_STS_SEGMENT_2); + C(RFRAME_CONFIG_SP3, + MCPS802154_LLHW_STS_SEGMENT_1 | MCPS802154_LLHW_STS_SEGMENT_2); + C(PRF_MODE_BPRF, MCPS802154_LLHW_BPRF); + C(PRF_MODE_HPRF, MCPS802154_LLHW_HPRF); + C(PREAMBLE_DURATION_32, MCPS802154_LLHW_PSR_32); + C(PREAMBLE_DURATION_64, MCPS802154_LLHW_PSR_64); + C(SFD_ID_0, MCPS802154_LLHW_SFD_4A); + C(SFD_ID_1, MCPS802154_LLHW_SFD_4Z_4); + C(SFD_ID_2, MCPS802154_LLHW_SFD_4Z_8); + C(SFD_ID_3, MCPS802154_LLHW_SFD_4Z_16); + C(SFD_ID_4, MCPS802154_LLHW_SFD_4Z_32); F(NUMBER_OF_STS_SEGMENTS_0); - F(NUMBER_OF_STS_SEGMENTS_1); - F(PSDU_DATA_RATE_6M81); - F(BPRF_PHR_DATA_RATE_850K); + C(NUMBER_OF_STS_SEGMENTS_1, MCPS802154_LLHW_STS_SEGMENT_1); + C(NUMBER_OF_STS_SEGMENTS_2, MCPS802154_LLHW_STS_SEGMENT_2); + C(NUMBER_OF_STS_SEGMENTS_3, MCPS802154_LLHW_STS_SEGMENT_3); + C(NUMBER_OF_STS_SEGMENTS_4, MCPS802154_LLHW_STS_SEGMENT_4); + C(PSDU_DATA_RATE_6M81, MCPS802154_LLHW_DATA_RATE_6M81); + C(PSDU_DATA_RATE_7M80, MCPS802154_LLHW_DATA_RATE_7M80); + C(PSDU_DATA_RATE_27M2, MCPS802154_LLHW_DATA_RATE_27M2); + C(PSDU_DATA_RATE_31M2, MCPS802154_LLHW_DATA_RATE_31M2); + C(BPRF_PHR_DATA_RATE_850K, MCPS802154_LLHW_PHR_DATA_RATE_850K); + C(BPRF_PHR_DATA_RATE_6M81, MCPS802154_LLHW_PHR_DATA_RATE_6M81); F(TX_ADAPTIVE_PAYLOAD_POWER); /* Antenna. */ P(RX_ANTENNA_PAIRS, u32, local->llhw->rx_antenna_pairs); P(TX_ANTENNAS, u32, local->llhw->tx_antennas); - /* STS and crypto capabilities. */ - F(STS_CONFIG_STATIC); /* Report. */ - F(AOA_AZIMUTH); - F(AOA_ELEVATION); - F(AOA_FOM); + C(AOA_AZIMUTH, MCPS802154_LLHW_AOA_AZIMUTH); + C(AOA_AZIMUTH_FULL, MCPS802154_LLHW_AOA_AZIMUTH_FULL); + C(AOA_ELEVATION, MCPS802154_LLHW_AOA_ELEVATION); + C(AOA_FOM, MCPS802154_LLHW_AOA_FOM); +#undef C #undef F #undef P @@ -1251,7 +1126,7 @@ nla_put_failure: return -ENOBUFS; } -int fira_session_control(struct fira_local *local, u32 call_id, +int fira_session_control(struct fira_local *local, enum fira_call call_id, const struct nlattr *params, const struct genl_info *info) { @@ -1275,6 +1150,7 @@ int fira_session_control(struct fira_local *local, u32 call_id, return -EINVAL; session_id = nla_get_u32(attrs[FIRA_CALL_ATTR_SESSION_ID]); + trace_region_fira_session_control(local, session_id, call_id); switch (call_id) { case FIRA_CALL_SESSION_INIT: diff --git a/mac/fira_region_call.h b/mac/fira_region_call.h index 778dcf3..670b6b3 100644 --- a/mac/fira_region_call.h +++ b/mac/fira_region_call.h @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2020-2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. @@ -26,7 +26,7 @@ #include "fira_region.h" /** - * fira_get_capabilities() - Get Fira capabilities. + * fira_get_capabilities() - Get FiRa capabilities. * @local: FiRa context. * @info: Request information. * @@ -36,15 +36,15 @@ int fira_get_capabilities(struct fira_local *local, const struct genl_info *info); /** - * fira_session_control() - Control Fira session. + * fira_session_control() - Control FiRa session. * @local: FiRa context. - * @call_id: Identifier of the fira procedure. + * @call_id: Identifier of the FiRa procedure. * @params: Nested attribute containing procedure parameters. * @info: Request information. * * Return: 0 or error. */ -int fira_session_control(struct fira_local *local, u32 call_id, +int fira_session_control(struct fira_local *local, enum fira_call call_id, const struct nlattr *params, const struct genl_info *info); diff --git a/mac/fira_round_hopping_crypto.h b/mac/fira_round_hopping_crypto.h index 3064673..0f0603a 100644 --- a/mac/fira_round_hopping_crypto.h +++ b/mac/fira_round_hopping_crypto.h @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2020-2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. @@ -38,7 +38,7 @@ struct fira_round_hopping_sequence; * Return: 0 or error. */ int fira_round_hopping_crypto_encrypt( - struct fira_round_hopping_sequence *round_hopping_sequence, + const struct fira_round_hopping_sequence *round_hopping_sequence, const u8 *data, u8 *out); /** diff --git a/mac/fira_round_hopping_sequence.c b/mac/fira_round_hopping_sequence.c index 9ce6c12..921f8cc 100644 --- a/mac/fira_round_hopping_sequence.c +++ b/mac/fira_round_hopping_sequence.c @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2020-2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. @@ -44,18 +44,18 @@ void fira_round_hopping_sequence_destroy(struct fira_session *session) fira_round_hopping_crypto_destroy(round_hopping_sequence); } -int fira_round_hopping_sequence_get(struct fira_session *session, +int fira_round_hopping_sequence_get(const struct fira_session *session, int block_index) { - struct fira_round_hopping_sequence *round_hopping_sequence = + const struct fira_session_params *params = &session->params; + const struct fira_round_hopping_sequence *round_hopping_sequence = &session->round_hopping_sequence; + int block_duration_slots = + params->block_duration_dtu / params->slot_duration_dtu; + int n_rounds = block_duration_slots / params->round_duration_slots; u8 block[AES_BLOCK_SIZE]; u8 out[AES_BLOCK_SIZE]; int r; - int block_duration_slots = session->params.block_duration_dtu / - session->params.slot_duration_dtu; - int n_rounds = - block_duration_slots / session->params.round_duration_slots; if (!block_index) return 0; diff --git a/mac/fira_round_hopping_sequence.h b/mac/fira_round_hopping_sequence.h index ca634b6..a89d91b 100644 --- a/mac/fira_round_hopping_sequence.h +++ b/mac/fira_round_hopping_sequence.h @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2020-2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. @@ -47,7 +47,7 @@ void fira_round_hopping_sequence_destroy(struct fira_session *session); * * Return: Round index. */ -int fira_round_hopping_sequence_get(struct fira_session *session, +int fira_round_hopping_sequence_get(const struct fira_session *session, int block_index); #endif /* NET_MCPS802154_FIRA_ROUND_HOPPING_SEQUENCE_H */ diff --git a/mac/fira_session.c b/mac/fira_session.c index 033641c..8373d0d 100644 --- a/mac/fira_session.c +++ b/mac/fira_session.c @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2020-2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. @@ -21,709 +21,915 @@ * Qorvo. Please contact Qorvo to inquire about licensing terms. */ -#include "fira_session.h" -#include "fira_crypto.h" -#include "fira_round_hopping_sequence.h" -#include "fira_access.h" -#include "fira_frame.h" -#include "fira_trace.h" - #include <linux/bitops.h> #include <linux/errno.h> #include <linux/ieee802154.h> #include <linux/string.h> #include <linux/limits.h> +#include <linux/math64.h> -#define FIRA_DRIFT_TOLERANCE_PPM 30 - -struct fira_session *fira_session_new(struct fira_local *local, u32 session_id) -{ - struct fira_session *session; +#include <net/mcps802154_frame.h> +#include <net/fira_region_nl.h> - session = kzalloc(sizeof(*session), GFP_KERNEL); - if (!session) - return NULL; +#include "fira_session.h" +#include "fira_crypto.h" +#include "fira_round_hopping_sequence.h" +#include "fira_access.h" +#include "fira_frame.h" +#include "fira_trace.h" - session->id = session_id; - session->params.ranging_round_usage = FIRA_RANGING_ROUND_USAGE_DSTWR; - session->params.short_addr = IEEE802154_ADDR_SHORT_BROADCAST; - session->params.controller_short_addr = IEEE802154_ADDR_SHORT_BROADCAST; - session->params.slot_duration_dtu = - FIRA_SLOT_DURATION_RSTU_DEFAULT * local->llhw->rstu_dtu; - session->params.block_duration_dtu = FIRA_BLOCK_DURATION_MS_DEFAULT * - (local->llhw->dtu_freq_hz / 1000); - session->params.round_duration_slots = - FIRA_ROUND_DURATION_SLOTS_DEFAULT; - session->params.max_rr_retry = FIRA_MAX_RR_RETRY_DEFAULT; - session->params.round_hopping = false; - session->params.priority = FIRA_PRIORITY_DEFAULT; - session->params.result_report_phase = true; - session->params.rframe_config = FIRA_RFRAME_CONFIG_SP3; - session->params.preamble_duration = FIRA_PREAMBULE_DURATION_64; - session->params.sfd_id = FIRA_SFD_ID_2; - - session->params._meas_seq_1.n_steps = 1; - session->params._meas_seq_1.steps[0].type = FIRA_MEASUREMENT_TYPE_RANGE; - session->params._meas_seq_1.steps[0].n_measurements = 1; - session->params._meas_seq_1.steps[0].rx_ant_set_nonranging = 0; - session->params._meas_seq_1.steps[0].rx_ant_sets_ranging[0] = 0; - session->params._meas_seq_1.steps[0].rx_ant_sets_ranging[1] = 0; - session->params._meas_seq_1.steps[0].tx_ant_set_nonranging = 0; - session->params._meas_seq_1.steps[0].tx_ant_set_ranging = 0; - session->params._meas_seq_2.n_steps = 0; - session->params.meas_seq.active = &(session->params._meas_seq_1); - session->params.meas_seq.current_step = 0; - session->params.meas_seq.n_measurements_achieved = 0; +inline static int +fira_compute_minimum_rssi(const struct fira_ranging_info *ranging_data) +{ + /* + * We want the WORST RSSI level. + * Please Note : RSSI is actually a negative number, but encoded + * as an absolute value. + */ + u8 min_rssi = 0; + int i; - /* Report parameters. */ - session->params.aoa_result_req = true; - session->params.report_tof = true; + for (i = 0; i < ranging_data->n_rx_rssis; i++) + min_rssi = max(ranging_data->rx_rssis[i], min_rssi); + return min_rssi; +} - if (fira_round_hopping_sequence_init(session)) - goto failed; +inline static int +fira_compute_average_rssi(const struct fira_ranging_info *ranging_data) +{ + unsigned sum; + int i; - list_add(&session->entry, &local->inactive_sessions); + if (!ranging_data->n_rx_rssis) + return 0; - return session; -failed: - kfree(session); - return NULL; + for (i = 0, sum = 0; i < ranging_data->n_rx_rssis; i++) + sum += ranging_data->rx_rssis[i]; + return sum / i; } -void fira_session_free(struct fira_session *session) +static int fira_report_local_aoa(struct sk_buff *msg, int nest_attr_id, + const struct fira_local_aoa_info *info) { - fira_round_hopping_sequence_destroy(session); - list_del(&session->entry); - fira_aead_destroy(&session->crypto.aead); - /* The session structure contains the Crypto context. This needs to be - * cleared. */ - kfree_sensitive(session); + struct nlattr *aoa; + + aoa = nla_nest_start(msg, nest_attr_id); + if (!aoa) + goto nla_put_failure; +#define A(x) FIRA_RANGING_DATA_MEASUREMENTS_AOA_ATTR_##x + if (nla_put_u8(msg, A(RX_ANTENNA_SET), info->rx_ant_set)) + goto nla_put_failure; + if (nla_put_s16(msg, A(AOA_2PI), info->aoa_2pi)) + goto nla_put_failure; + if (nla_put_s16(msg, A(PDOA_2PI), info->pdoa_2pi)) + goto nla_put_failure; + if (nla_put_u8(msg, A(AOA_FOM), info->aoa_fom)) + goto nla_put_failure; +#undef A + nla_nest_end(msg, aoa); + return 0; +nla_put_failure: + return -EMSGSIZE; } -struct fira_session *fira_session_get(struct fira_local *local, u32 session_id, - bool *active) +inline static int fira_session_report_measurement( + const struct fira_session *session, struct sk_buff *msg, + const struct fira_ranging_info *ranging_data, s64 rctu_freq_hz) { - struct fira_session *session; - - list_for_each_entry (session, &local->inactive_sessions, entry) { - if (session->id == session_id) { - *active = false; - return session; + const struct fira_session_params *params = &session->params; + bool report_rssi_val_present = false; + int report_rssi_val = 0; + + if (params->report_rssi && + ranging_data->status == FIRA_STATUS_RANGING_SUCCESS) { + switch (params->report_rssi) { + case FIRA_RSSI_REPORT_MINIMUM: + report_rssi_val_present = true; + report_rssi_val = + fira_compute_minimum_rssi(ranging_data); + break; + case FIRA_RSSI_REPORT_AVERAGE: + report_rssi_val_present = true; + report_rssi_val = + fira_compute_average_rssi(ranging_data); + break; + default: + break; } } - list_for_each_entry (session, &local->active_sessions, entry) { - if (session->id == session_id) { - *active = true; - return session; +#define A(x) FIRA_RANGING_DATA_MEASUREMENTS_ATTR_##x + + if (nla_put_u16(msg, A(SHORT_ADDR), ranging_data->short_addr) || + nla_put_u8(msg, A(STATUS), ranging_data->status)) + goto nla_put_failure; + + if (ranging_data->status) { + if (nla_put_u8(msg, A(SLOT_INDEX), ranging_data->slot_index)) + goto nla_put_failure; + return 0; + } + if (ranging_data->tof_present) { + static const s64 speed_of_light_mm_per_s = 299702547000ull; + s32 distance_mm = div64_s64(ranging_data->tof_rctu * + speed_of_light_mm_per_s, + rctu_freq_hz); + if (nla_put_s32(msg, A(DISTANCE_MM), distance_mm)) + goto nla_put_failure; + } + if (ranging_data->local_aoa.present) { + if (fira_report_local_aoa(msg, A(LOCAL_AOA), + &ranging_data->local_aoa)) + goto nla_put_failure; + } + if (ranging_data->local_aoa_azimuth.present) { + if (fira_report_local_aoa(msg, A(LOCAL_AOA_AZIMUTH), + &ranging_data->local_aoa_azimuth)) + goto nla_put_failure; + } + if (ranging_data->local_aoa_elevation.present) { + if (fira_report_local_aoa(msg, A(LOCAL_AOA_ELEVATION), + &ranging_data->local_aoa_elevation)) + goto nla_put_failure; + } + if (ranging_data->remote_aoa_azimuth_present) { + if (nla_put_s16(msg, A(REMOTE_AOA_AZIMUTH_2PI), + ranging_data->remote_aoa_azimuth_2pi)) + goto nla_put_failure; + if (ranging_data->remote_aoa_fom_present) { + if (nla_put_u8(msg, A(REMOTE_AOA_AZIMUTH_FOM), + ranging_data->remote_aoa_azimuth_fom)) + goto nla_put_failure; } } + if (ranging_data->remote_aoa_elevation_present) { + if (nla_put_s16(msg, A(REMOTE_AOA_ELEVATION_PI), + ranging_data->remote_aoa_elevation_pi)) + goto nla_put_failure; + if (ranging_data->remote_aoa_fom_present) { + if (nla_put_u8(msg, A(REMOTE_AOA_ELEVATION_FOM), + ranging_data->remote_aoa_elevation_fom)) + goto nla_put_failure; + } + } + if (report_rssi_val_present) { + if (nla_put_u8(msg, A(RSSI), report_rssi_val)) + goto nla_put_failure; + } + if (ranging_data->data_payload_len > 0) { + if (nla_put(msg, A(DATA_PAYLOAD_RECV), + ranging_data->data_payload_len, + ranging_data->data_payload)) + goto nla_put_failure; + } + if (session->data_payload.sent) { + if (nla_put_u32(msg, A(DATA_PAYLOAD_SEQ_SENT), + session->data_payload.seq)) + goto nla_put_failure; + } - return NULL; +#undef A + return 0; +nla_put_failure: + return -EMSGSIZE; } -void fira_session_copy_controlees(struct fira_controlees_array *to, - const struct fira_controlees_array *from) +inline static int fira_report_measurement_stopped_controlee(struct sk_buff *msg, + __le16 short_addr) { - /* Copy only valid entries. */ - memcpy(to->data, from->data, from->size * sizeof(from->data[0])); - to->size = from->size; +#define A(x) FIRA_RANGING_DATA_MEASUREMENTS_ATTR_##x + + if (nla_put_u16(msg, A(SHORT_ADDR), short_addr) || + nla_put_u8(msg, A(STOPPED), 1)) + goto nla_put_failure; + +#undef A + return 0; +nla_put_failure: + return -EMSGSIZE; } -int fira_session_set_controlees(struct fira_local *local, - struct fira_session *session, - struct fira_controlees_array *controlees_array, - const struct fira_controlee *controlees, - size_t n_controlees) +inline static int +fira_session_report_ranging_data(const struct fira_session *session, + const struct fira_report_info *report_info, + int dtu_freq_hz, int dtu_rctu, + struct sk_buff *msg) { + const struct fira_session_params *params = &session->params; + struct nlattr *data, *measurements, *measurement; + int ranging_interval_ms = params->block_duration_dtu * + (session->block_stride_len + 1) / + (dtu_freq_hz / 1000); + s64 rctu_freq_hz = (s64)dtu_freq_hz * dtu_rctu; + int i; - if (!fira_frame_check_n_controlees(session, n_controlees, false)) - return -EINVAL; + data = nla_nest_start(msg, FIRA_CALL_ATTR_RANGING_DATA); + if (!data) + goto nla_put_failure; - for (i = 0; i < n_controlees; i++) - controlees_array->data[i] = controlees[i]; + if (nla_put_u32(msg, FIRA_RANGING_DATA_ATTR_BLOCK_INDEX, + session->block_index) || + nla_put_u32(msg, FIRA_RANGING_DATA_ATTR_RANGING_INTERVAL_MS, + ranging_interval_ms)) + goto nla_put_failure; - controlees_array->size = n_controlees; + if (report_info->stopped) { + enum fira_ranging_data_attrs_stopped_values stopped; - return 0; -} + if (session->stop_no_response) + stopped = FIRA_RANGING_DATA_ATTR_STOPPED_NO_RESPONSE; + else if (session->stop_inband) + stopped = FIRA_RANGING_DATA_ATTR_STOPPED_IN_BAND; + else + stopped = FIRA_RANGING_DATA_ATTR_STOPPED_REQUEST; -int fira_session_new_controlees(struct fira_session *session, bool active, - struct fira_controlees_array *controlees_array, - const struct fira_controlee *controlees, - size_t n_controlees) -{ - int i, j; + if (nla_put_u8(msg, FIRA_RANGING_DATA_ATTR_STOPPED, stopped)) + goto nla_put_failure; - if (!fira_frame_check_n_controlees( - session, controlees_array->size + n_controlees, active)) - return -EINVAL; + /* + Case where measurements are not available: + - A controller stop request. + - A controller max measurements reached. + - A controlee stop in band. + */ + if ((session->params.device_type == + FIRA_DEVICE_TYPE_CONTROLLER && + stopped == FIRA_RANGING_DATA_ATTR_STOPPED_REQUEST) || + (session->params.device_type == + FIRA_DEVICE_TYPE_CONTROLEE && + stopped == FIRA_RANGING_DATA_ATTR_STOPPED_IN_BAND)) + goto end_report; + } - for (i = 0; i < n_controlees; i++) { - for (j = 0; j < controlees_array->size; j++) { - if (controlees[i].short_addr == - controlees_array->data[j].short_addr) - return -EINVAL; + if (report_info->n_ranging_data + report_info->n_stopped_controlees) { + measurements = nla_nest_start( + msg, FIRA_RANGING_DATA_ATTR_MEASUREMENTS); + if (!measurements) + goto nla_put_failure; + + for (i = 0; i < report_info->n_ranging_data; i++) { + measurement = nla_nest_start(msg, 1); + if (fira_session_report_measurement( + session, msg, &report_info->ranging_data[i], + rctu_freq_hz)) + goto nla_put_failure; + nla_nest_end(msg, measurement); + } + + for (i = 0; i < report_info->n_stopped_controlees; i++) { + measurement = nla_nest_start(msg, 1); + if (fira_report_measurement_stopped_controlee( + msg, report_info->stopped_controlees[i])) + goto nla_put_failure; + nla_nest_end(msg, measurement); } - } - for (i = 0; i < n_controlees; i++) - controlees_array->data[controlees_array->size++] = - controlees[i]; + nla_nest_end(msg, measurements); + } +end_report: + nla_nest_end(msg, data); return 0; + +nla_put_failure: + return -EMSGSIZE; } -static void -fira_session_update_stopping_controlees(struct fira_session *session) +static int +fira_session_report_diagnostic_rssi(const struct fira_diagnostic *diagnostic, + struct sk_buff *msg) { - size_t ii, io; - struct fira_controlees_array *controlees_array = - &session->current_controlees; - - for (ii = 0, io = 0; ii < controlees_array->size; ii++) { - struct fira_controlee *c = &controlees_array->data[ii]; - - if (c->state != FIRA_CONTROLEE_STATE_PENDING_DEL) { - if (io != ii) - controlees_array->data[io] = *c; - controlees_array->data[io].state = - FIRA_CONTROLEE_STATE_RUNNING; - io++; - } + struct nlattr *nest; + int i; + + nest = nla_nest_start( + msg, FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_RSSIS); + if (!nest) + goto nla_put_failure; + for (i = 0; i < diagnostic->n_rssis; i++) { + if (nla_put_u8(msg, i, diagnostic->rssis_q1[i])) + goto nla_put_failure; } - controlees_array->size = io; + nla_nest_end(msg, nest); + return 0; + +nla_put_failure: + return -EMSGSIZE; } -int fira_session_del_controlees(struct fira_controlees_array *controlees_array, - const struct fira_controlee *controlees, - size_t n_controlees) +static int +fira_session_report_diagnostic_aoa(const struct fira_diagnostic *diagnostic, + struct sk_buff *msg) { - size_t ii, io, j; - - for (ii = 0, io = 0; ii < controlees_array->size; ii++) { - bool remove = false; - struct fira_controlee *c = &controlees_array->data[ii]; - - for (j = 0; j < n_controlees && !remove; j++) { - if (c->short_addr == controlees[j].short_addr) - remove = true; - } + const struct mcps802154_rx_aoa_measurements *aoa; + struct nlattr *aoas, *aoa_report; + int i; - if (!remove) { - if (io != ii) - controlees_array->data[io] = *c; - io++; - } + aoas = nla_nest_start(msg, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_AOAS); + if (!aoas) + goto nla_put_failure; + for (i = 0; i < diagnostic->n_aoas; i++) { + aoa = &diagnostic->aoas[i]; + + aoa_report = nla_nest_start(msg, i); + if (!aoa_report) + goto nla_put_failure; +#define A(x) FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_AOAS_ATTR_##x + if (nla_put_s16(msg, A(TDOA), aoa->tdoa_rctu)) + goto nla_put_failure; + if (nla_put_s16(msg, A(PDOA), aoa->pdoa_rad_q11)) + goto nla_put_failure; + if (nla_put_s16(msg, A(AOA), aoa->aoa_rad_q11)) + goto nla_put_failure; + if (nla_put_u8(msg, A(FOM), aoa->fom)) + goto nla_put_failure; + if (nla_put_u8(msg, A(TYPE), aoa->type)) + goto nla_put_failure; +#undef A + nla_nest_end(msg, aoa_report); } - controlees_array->size = io; - + nla_nest_end(msg, aoas); return 0; + +nla_put_failure: + return -EMSGSIZE; } -int fira_session_async_del_controlees( - struct fira_session *session, - struct fira_controlees_array *controlees_array, - const struct fira_controlee *controlees, size_t n_controlees) +static int fira_session_report_cir_samples(const struct mcps802154_rx_cir *cir, + struct sk_buff *msg) { - size_t i, j; + const struct mcps802154_rx_cir_sample_window *sw = &cir->sample_window; + const u8 *samples = sw->samples; + struct nlattr *sample_nest; + int i; - for (i = 0; i < controlees_array->size; i++) { - struct fira_controlee *c = &controlees_array->data[i]; - enum fira_controlee_state state = FIRA_CONTROLEE_STATE_RUNNING; + sample_nest = nla_nest_start( + msg, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_FP_SAMPLE_WINDOW); + if (!sample_nest) + goto nla_put_failure; + for (i = 0; i < sw->n_samples; i++) { + const u8 *sample = &samples[i * sw->sizeof_sample]; - for (j = 0; j < n_controlees; j++) { - if (c->short_addr == controlees[j].short_addr) { - state = FIRA_CONTROLEE_STATE_PENDING_DEL; - session->controlee_management_flags |= - FIRA_SESSION_CONTROLEE_MANAGEMENT_FLAG_STOP; - break; - } - } - c->state = state; + if (nla_put(msg, i, sw->sizeof_sample, sample)) + goto nla_put_failure; } - + nla_nest_end(msg, sample_nest); return 0; + +nla_put_failure: + return -EMSGSIZE; } -void fira_session_stop_controlees(struct fira_session *session, - struct fira_controlees_array *controlees_array) +static int +fira_session_report_diagnostic_cir(const struct fira_diagnostic *diagnostic, + struct sk_buff *msg) { - size_t i; + struct nlattr *cirs_nest, *cir_nest; + int i; - for (i = 0; i < controlees_array->size; i++) { - controlees_array->data[i].state = - FIRA_CONTROLEE_STATE_PENDING_STOP; + cirs_nest = nla_nest_start( + msg, FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_CIRS); + if (!cirs_nest) + goto nla_put_failure; + for (i = 0; i < diagnostic->n_cirs; i++) { + struct mcps802154_rx_cir *cir = &diagnostic->cirs[i]; + + cir_nest = nla_nest_start(msg, i); + if (!cir_nest) + goto nla_put_failure; +#define A(x) FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_##x + if (nla_put_u16(msg, A(FP_IDX), cir->fp_index)) + goto nla_put_failure; + if (nla_put_s16(msg, A(FP_SNR), cir->fp_snr)) + goto nla_put_failure; + if (nla_put_u16(msg, A(FP_NS), cir->fp_ns_q6)) + goto nla_put_failure; + if (nla_put_u16(msg, A(PP_IDX), cir->pp_index)) + goto nla_put_failure; + if (nla_put_s16(msg, A(PP_SNR), cir->pp_snr)) + goto nla_put_failure; + if (nla_put_u16(msg, A(PP_NS), cir->pp_ns_q6)) + goto nla_put_failure; + if (nla_put_u16(msg, A(FP_SAMPLE_OFFSET), + cir->fp_sample_offset)) + goto nla_put_failure; +#undef A + if (fira_session_report_cir_samples(cir, msg)) + goto nla_put_failure; + nla_nest_end(msg, cir_nest); } + nla_nest_end(msg, cirs_nest); + return 0; + +nla_put_failure: + return -EMSGSIZE; } -bool fira_session_is_ready(struct fira_local *local, - struct fira_session *session) +static int fira_session_report_frame_diagnostics( + const struct fira_session *session, + const struct fira_report_info *report_info, struct sk_buff *msg) { - int round_duration_dtu; - struct fira_session_params *params = &session->params; + const struct fira_session_params *params = &session->params; + struct nlattr *frame_nest, *reports_nest; + bool is_controller = params->device_type == FIRA_DEVICE_TYPE_CONTROLLER; + int i; - if (params->multi_node_mode == FIRA_MULTI_NODE_MODE_UNICAST) { - if (session->current_controlees.size > 1) - return false; - } else { - /* on success, session will become active, so assume it is */ - if (!fira_frame_check_n_controlees( - session, session->current_controlees.size, true)) - return false; + frame_nest = nla_nest_start( + msg, FIRA_RANGING_DIAGNOSTICS_ATTR_FRAME_REPORTS); + if (!frame_nest) + goto nla_put_failure; + + for (i = 0; i < report_info->n_slots; i++) { + const struct fira_slot *slot = &report_info->slots[i]; + int is_tx = slot->controller_tx ? is_controller : + !is_controller; + + reports_nest = nla_nest_start(msg, i); + if (!reports_nest) + goto nla_put_failure; +#define A(x) FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_##x + if (nla_put_u8(msg, A(ANT_SET), + is_tx ? slot->tx_ant_set : slot->rx_ant_set)) + goto nla_put_failure; + if (nla_put_u8(msg, A(ACTION), is_tx)) + goto nla_put_failure; + if (nla_put_u8(msg, A(MSG_ID), slot->message_id)) + goto nla_put_failure; +#undef A + /* Specific reports are done for Rx frames only. */ + if (!is_tx) { + const struct fira_diagnostic *diagnostic = + &report_info->diagnostics[i]; + + if (params->diagnostic_report_flags & + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORT_RSSIS) { + if (fira_session_report_diagnostic_rssi( + diagnostic, msg)) + goto nla_put_failure; + } + if (params->diagnostic_report_flags & + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORT_AOAS) { + if (fira_session_report_diagnostic_aoa( + diagnostic, msg)) + goto nla_put_failure; + } + if (params->diagnostic_report_flags & + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORT_CIRS) { + if (fira_session_report_diagnostic_cir( + diagnostic, msg)) + goto nla_put_failure; + } + } + nla_nest_end(msg, reports_nest); } + nla_nest_end(msg, frame_nest); + return 0; - round_duration_dtu = - params->slot_duration_dtu * params->round_duration_slots; - return params->slot_duration_dtu != 0 && - params->block_duration_dtu != 0 && - params->round_duration_slots != 0 && - round_duration_dtu < params->block_duration_dtu; +nla_put_failure: + return -EMSGSIZE; } -inline static void fira_update_meas_seq(struct fira_session *session) +static inline int fira_session_report_ranging_diagnostics( + const struct fira_session *session, + const struct fira_report_info *report_info, struct sk_buff *msg) { - struct fira_session_params *p = &session->params; - if (p->meas_seq.update_flag) { - if (p->meas_seq.active == &p->_meas_seq_1) - p->meas_seq.active = &p->_meas_seq_2; - else - p->meas_seq.active = &p->_meas_seq_1; - p->meas_seq.current_step = 0; - p->meas_seq.n_measurements_achieved = 0; - p->meas_seq.update_flag = false; - } else { - if (p->meas_seq.n_measurements_achieved >= - p->meas_seq.active->steps[p->meas_seq.current_step] - .n_measurements) { - p->meas_seq.n_measurements_achieved = 0; - p->meas_seq.current_step++; - p->meas_seq.current_step %= p->meas_seq.active->n_steps; - } - } - trace_region_fira_meas_seq_step( - session, &(p->meas_seq.active->steps[p->meas_seq.current_step]), - p->meas_seq.current_step); + const struct fira_session_params *params = &session->params; + struct nlattr *diagnostics_nest; + + if (!params->report_diagnostics) + return 0; + + diagnostics_nest = + nla_nest_start(msg, FIRA_CALL_ATTR_RANGING_DIAGNOSTICS); + if (!diagnostics_nest) + goto nla_put_failure; + + if (fira_session_report_frame_diagnostics(session, report_info, msg)) + goto nla_put_failure; + + nla_nest_end(msg, diagnostics_nest); + return 0; + +nla_put_failure: + return -EMSGSIZE; } -void fira_session_prepare(struct fira_session *session) +struct fira_session *fira_session_new(struct fira_local *local, u32 session_id) { - fira_update_meas_seq(session); - if (session->params.device_type == FIRA_DEVICE_TYPE_CONTROLLER) { - session->next_block_stride_len = - session->params.block_stride_len; - if (session->params.round_hopping) { - u32 next_block_index = session->block_index + - session->next_block_stride_len + - 1; - session->next_round_index = - fira_round_hopping_sequence_get( - session, next_block_index); + struct fira_session *session; + struct fira_session_params *params; + int all_rx_ctx_size = FIRA_CONTROLEES_MAX * local->llhw->rx_ctx_size; + void *rx_ctx_base = NULL; + int i; + + session = kzalloc(sizeof(*session), GFP_KERNEL); + if (!session) + return NULL; + if (all_rx_ctx_size) { + rx_ctx_base = kzalloc(all_rx_ctx_size, GFP_KERNEL); + if (!rx_ctx_base) + goto failed; + } + + params = &session->params; + session->id = session_id; + session->measurements.reset = true; + + /* Explicit default parameters as implicit is zero. */ + params->ranging_round_usage = FIRA_RANGING_ROUND_USAGE_DSTWR; + params->short_addr = IEEE802154_ADDR_SHORT_BROADCAST; + params->controller_short_addr = IEEE802154_ADDR_SHORT_BROADCAST; + params->slot_duration_dtu = + FIRA_SLOT_DURATION_RSTU_DEFAULT * local->llhw->rstu_dtu; + params->block_duration_dtu = FIRA_BLOCK_DURATION_MS_DEFAULT * + (local->llhw->dtu_freq_hz / 1000); + params->round_duration_slots = FIRA_ROUND_DURATION_SLOTS_DEFAULT; + params->max_rr_retry = FIRA_MAX_RR_RETRY_DEFAULT; + params->round_hopping = false; + params->priority = FIRA_PRIORITY_DEFAULT; + params->sts_length = FIRA_STS_LENGTH_64; + params->rframe_config = FIRA_RFRAME_CONFIG_SP3; + params->preamble_duration = FIRA_PREAMBULE_DURATION_64; + params->sfd_id = FIRA_SFD_ID_2; + params->number_of_sts_segments = FIRA_STS_SEGMENTS_1; + params->meas_seq.n_steps = 1; + params->meas_seq.steps[0].type = FIRA_MEASUREMENT_TYPE_RANGE; + params->meas_seq.steps[0].n_measurements = 1; + params->meas_seq.steps[0].rx_ant_set_nonranging = 0; + params->meas_seq.steps[0].rx_ant_sets_ranging[0] = 0; + params->meas_seq.steps[0].rx_ant_sets_ranging[1] = 0; + params->meas_seq.steps[0].tx_ant_set_nonranging = 0; + params->meas_seq.steps[0].tx_ant_set_ranging = 0; + /* Report parameters. */ + params->aoa_result_req = true; + params->report_tof = true; + params->result_report_phase = true; + params->range_data_ntf_config = FIRA_RANGE_DATA_NTF_ALWAYS; + params->range_data_ntf_proximity_near_mm = 0; + params->range_data_ntf_proximity_far_mm = + FIRA_RANGE_DATA_NTF_PROXIMITY_FAR_DEFAULT; + + if (fira_round_hopping_sequence_init(session)) + goto failed; + + if (all_rx_ctx_size) { + for (i = 0; i < FIRA_CONTROLEES_MAX; i++) { + void *rx_ctx = (char *)rx_ctx_base + + i * local->llhw->rx_ctx_size; + session->rx_ctx[i] = rx_ctx; } } + + INIT_LIST_HEAD(&session->current_controlees); + + fira_session_fsm_initialise(local, session); + return session; + +failed: + kfree(rx_ctx_base); + kfree(session); + return NULL; } -void fira_session_update_round_index(struct fira_session *session) +void fira_session_free(struct fira_local *local, struct fira_session *session) { - if (session->hopping_sequence_generation) { - session->round_index = fira_round_hopping_sequence_get( - session, session->block_index); - } else { - session->round_index = session->next_round_index; - session->hopping_sequence_generation = - session->params.round_hopping; + struct fira_controlee *controlee, *tmp_controlee; + + list_for_each_entry_safe (controlee, tmp_controlee, + &session->current_controlees, entry) { + list_del(&controlee->entry); + kfree(controlee); } + fira_session_fsm_uninit(local, session); + fira_round_hopping_sequence_destroy(session); + fira_aead_destroy(&session->crypto.aead); + /* + * The session structure contains the Crypto context. This needs to be + * cleared. + */ + kfree(session->rx_ctx[0]); + kfree_sensitive(session); } -static void fira_session_update(struct fira_local *local, +int fira_session_set_controlees(struct fira_local *local, struct fira_session *session, - u32 next_timestamp_dtu) + struct list_head *controlees, int n_controlees) { - u32 access_dtu; - s32 diff_dtu; - int block_duration_margin_dtu = 0; - - if (session->params.device_type == FIRA_DEVICE_TYPE_CONTROLEE) - block_duration_margin_dtu = - fira_session_get_block_duration_margin(local, session); - - /* Do we have the time to participate in the current block? */ - access_dtu = session->block_start_dtu + - fira_session_get_round_slot(session) * - session->params.slot_duration_dtu - - block_duration_margin_dtu; - diff_dtu = access_dtu - next_timestamp_dtu; - - if (diff_dtu < 0) { - int block_duration_dtu = session->params.block_duration_dtu; - int block_duration_slots = - block_duration_dtu / session->params.slot_duration_dtu; - int block_stride_len = session->block_stride_len; - int block_stride_duration_dtu = - block_duration_dtu * (block_stride_len + 1); - int add_blocks, add_strides; + struct fira_controlee *controlee, *tmp_controlee; - /* - * No time in current block, which block should we try? The - * result of this can be 0, meaning that we are still in the - * same block, but the access was earlier in this block. - */ - diff_dtu = session->block_start_dtu - - block_duration_margin_dtu - next_timestamp_dtu; - add_strides = -diff_dtu / block_stride_duration_dtu; - add_blocks = add_strides * (block_stride_len + 1); - - session->block_start_dtu += add_blocks * block_duration_dtu; - session->block_index += add_blocks; - session->sts_index += add_blocks * block_duration_slots; - if (add_blocks != 0) { - /* - * More than one ranging round skipped, can not trust - * last hopping instructions. - */ - if (add_blocks > block_stride_len + 1) - session->hopping_sequence_generation = - session->params.round_hopping; - fira_session_update_round_index(session); - } + if (!fira_frame_check_n_controlees(session, n_controlees, false)) + return -EINVAL; - /* Retry in the found block. */ - access_dtu = session->block_start_dtu + - fira_session_get_round_slot(session) * - session->params.slot_duration_dtu - - block_duration_margin_dtu; - diff_dtu = access_dtu - next_timestamp_dtu; - - if (diff_dtu < 0) { - /* Still no time, next one will be OK. */ - add_blocks = block_stride_len + 1; - session->block_start_dtu += - add_blocks * block_duration_dtu; - session->block_index += add_blocks; - session->sts_index += add_blocks * block_duration_slots; - fira_session_update_round_index(session); - } + list_for_each_entry_safe (controlee, tmp_controlee, + &session->current_controlees, entry) { + list_del(&controlee->entry); + kfree(controlee); + } + list_for_each_entry_safe (controlee, tmp_controlee, controlees, entry) { + list_move_tail(&controlee->entry, &session->current_controlees); } + session->n_current_controlees = n_controlees; + return 0; } -static inline bool -fira_session_has_higher_priority(const struct fira_session *session, - const struct fira_session *selected_session) +int fira_session_new_controlees(struct fira_session *session, + struct list_head *controlees, int n_controlees, + bool async) { - return session->params.priority > selected_session->params.priority || - (session->params.priority == selected_session->params.priority && - is_before_dtu(session->last_access_timestamp_dtu, - selected_session->last_access_timestamp_dtu)); -} + struct fira_controlee *controlee, *new_controlee, *tmp_new_controlee; -static struct fira_session *fira_session_find_next(struct fira_local *local, - u32 next_timestamp_dtu, - u32 max_access_duration_dtu, - u32 *timestamp_dtu, - u32 *duration_dtu) -{ - struct fira_session *selected_session = NULL; - struct fira_session *session; - u32 selected_timestamp_dtu = 0; - u32 selected_duration_dtu = 0; - u32 access_timestamp_dtu; - u32 access_duration_dtu; - u32 unsync_access_duration_dtu; - u32 selected_unsync_access_duration_dtu = 0; - u32 max_unsync_access_duration_dtu = 0; - bool found_sync_session = false; - struct mcps802154_region_demand demand; - - /* Select the next synchronised session that can be scheduled without - * overlapping any other synchronised sessions or if they are - * overlapping, the session with the highest priority. */ - list_for_each_entry (session, &local->active_sessions, entry) { - if (session->params.device_type == FIRA_DEVICE_TYPE_CONTROLEE && - !session->synchronised) - continue; - fira_session_update(local, session, next_timestamp_dtu); - fira_session_get_demand(local, session, &demand); - access_timestamp_dtu = demand.timestamp_dtu; - access_duration_dtu = demand.max_duration_dtu; - if ((!selected_session || - is_before_dtu(access_timestamp_dtu + access_duration_dtu + - local->llhw->anticip_dtu, - selected_timestamp_dtu + 1) || - (is_before_dtu(access_timestamp_dtu, - selected_timestamp_dtu + - selected_duration_dtu + - local->llhw->anticip_dtu) && - fira_session_has_higher_priority(session, - selected_session))) && - (!max_access_duration_dtu || - access_duration_dtu <= max_access_duration_dtu)) { - found_sync_session = true; - selected_session = session; - selected_timestamp_dtu = access_timestamp_dtu; - selected_duration_dtu = access_duration_dtu; + if (!fira_frame_check_n_controlees( + session, session->n_current_controlees + n_controlees, + async)) + return -EINVAL; + + list_for_each_entry (new_controlee, controlees, entry) { + list_for_each_entry (controlee, &session->current_controlees, + entry) { + if (new_controlee->short_addr == controlee->short_addr) + return -EINVAL; } } - if (found_sync_session) - max_unsync_access_duration_dtu = - max((s32)(selected_timestamp_dtu - next_timestamp_dtu - - local->llhw->anticip_dtu), - 0); - - /* Select a session that is not synchronised if there is enough time to - * schedule it before the synchronised session currently selected. */ - list_for_each_entry (session, &local->active_sessions, entry) { - if (session->params.device_type != FIRA_DEVICE_TYPE_CONTROLEE || - session->synchronised) - continue; - fira_session_update(local, session, next_timestamp_dtu); - fira_session_get_demand(local, session, &demand); - access_duration_dtu = demand.max_duration_dtu; - unsync_access_duration_dtu = U32_MAX; - if (session->params.max_rr_retry) { - int nb_blocks = - session->params.max_rr_retry * - (session->block_stride_len + 1) + - session->last_block_index - - session->block_index; - - unsync_access_duration_dtu = - min((u32)session->params.block_duration_dtu * - max(nb_blocks, 1), - unsync_access_duration_dtu); - } - if (found_sync_session) - unsync_access_duration_dtu = - min(max_unsync_access_duration_dtu, - unsync_access_duration_dtu); - if (max_access_duration_dtu) - unsync_access_duration_dtu = - min(max_access_duration_dtu, - unsync_access_duration_dtu); - /* Among the sessions that are not synchronised, select the one for which the - * shortest access needs to be generated. */ - if (access_duration_dtu <= unsync_access_duration_dtu && - (!selected_unsync_access_duration_dtu || - unsync_access_duration_dtu < - selected_unsync_access_duration_dtu)) { - selected_session = session; - selected_timestamp_dtu = next_timestamp_dtu; - if (unsync_access_duration_dtu != U32_MAX) { - selected_unsync_access_duration_dtu = - selected_duration_dtu = - unsync_access_duration_dtu; - } else { - selected_unsync_access_duration_dtu = - selected_duration_dtu = 0; - } - } + list_for_each_entry_safe (new_controlee, tmp_new_controlee, controlees, + entry) { + if (async) + new_controlee->state = FIRA_CONTROLEE_STATE_PENDING_RUN; + list_move_tail(&new_controlee->entry, + &session->current_controlees); + session->n_current_controlees++; } - *timestamp_dtu = selected_timestamp_dtu; - *duration_dtu = selected_duration_dtu; - return selected_session; + return 0; } -static void -fira_session_check_max_number_of_measurements(struct fira_local *local, - struct fira_session *session) +int fira_session_del_controlees(struct fira_session *session, + struct list_head *controlees, bool async) { - if (!session->max_number_of_measurements_reached && - session->params.max_number_of_measurements && - ((s32)(session->params.max_number_of_measurements - - session->number_of_measurements) <= 0)) { - session->max_number_of_measurements_reached = true; - session->controlee_management_flags = - FIRA_SESSION_CONTROLEE_MANAGEMENT_FLAG_STOP; - fira_session_stop_controlees(session, - &session->current_controlees); + struct fira_controlee *controlee, *new_controlee, *tmp_controlee, + *tmp_new_controlee; + + list_for_each_entry_safe (new_controlee, tmp_new_controlee, controlees, + entry) { + list_for_each_entry_safe (controlee, tmp_controlee, + &session->current_controlees, entry) { + if (new_controlee->short_addr == + controlee->short_addr) { + if (async) { + controlee->state = + FIRA_CONTROLEE_STATE_PENDING_DEL; + } else { + list_del(&controlee->entry); + kfree(controlee); + session->n_current_controlees--; + } + break; + } + } + list_del(&new_controlee->entry); + kfree(new_controlee); } + return 0; } -static bool fira_session_check_max_rr_retry(struct fira_session *session) +void fira_session_stop_controlees(struct fira_session *session) { - if (session->params.max_rr_retry && - !((s32)((session->block_index - session->last_block_index) / - (session->block_stride_len + 1) - - session->params.max_rr_retry) < 0)) { - session->stop_no_response = true; - return true; + struct fira_controlee *controlee; + + list_for_each_entry (controlee, &session->current_controlees, entry) { + controlee->state = FIRA_CONTROLEE_STATE_PENDING_STOP; } - return false; } -static void -fira_session_send_collision_reports(struct fira_local *local, - struct fira_session *selected_session, - u32 selected_end_dtu) +void fira_session_restart_controlees(struct fira_session *session) { - struct fira_session *session; - struct fira_session *tmp_session; - struct mcps802154_region_demand demand; - int i; + struct fira_controlee *controlee; - list_for_each_entry_safe (session, tmp_session, &local->active_sessions, - entry) { - if (session == selected_session || - (session->params.device_type == - FIRA_DEVICE_TYPE_CONTROLEE && - !session->synchronised)) - continue; - fira_session_get_demand(local, session, &demand); - if (is_before_dtu(demand.timestamp_dtu, selected_end_dtu)) { - fira_compute_access(local, session); - for (i = 0; i < local->n_ranging_info; i++) { - local->ranging_info[i].status = - FIRA_STATUS_RANGING_TX_FAILED; - } - fira_session_access_done(local, session, true); - } + list_for_each_entry (controlee, &session->current_controlees, entry) { + if (controlee->state != FIRA_CONTROLEE_STATE_PENDING_DEL && + controlee->state != FIRA_CONTROLEE_STATE_DELETING) + controlee->state = FIRA_CONTROLEE_STATE_RUNNING; } } -static void fira_session_stop_expired_sessions(struct fira_local *local) +int fira_session_controlees_running_count(const struct fira_session *session) { - struct fira_session *session; - struct fira_session *tmp_session; - int i; - - list_for_each_entry_safe (session, tmp_session, &local->active_sessions, - entry) { - if (session == local->current_session || - !fira_session_check_max_rr_retry(session)) - continue; - fira_compute_access(local, session); - for (i = 0; i < local->n_ranging_info; i++) { - local->ranging_info[i].status = - FIRA_STATUS_RANGING_RX_TIMEOUT; - } - fira_session_access_done(local, session, true); + struct fira_controlee *controlee; + int count = 0; + + list_for_each_entry (controlee, &session->current_controlees, entry) { + if (controlee->state == FIRA_CONTROLEE_STATE_RUNNING || + controlee->state == FIRA_CONTROLEE_STATE_PENDING_STOP || + controlee->state == FIRA_CONTROLEE_STATE_PENDING_DEL) + count++; } + return count; } -static void fira_session_check_unsync(struct fira_local *local, - struct fira_session *session) +void fira_session_update_controlees(struct fira_local *local, + struct fira_session *session) { - int nb_blocks; - int unsync_drift_dtu; - int block_margin_dtu; + struct fira_controlee *controlee, *tmp_controlee; + bool reset_rx_ctx = false; + int i; - if ((session->params.device_type != FIRA_DEVICE_TYPE_CONTROLEE) || - !session->synchronised) - return; + list_for_each_entry_safe (controlee, tmp_controlee, + &session->current_controlees, entry) { + if (controlee->state == FIRA_CONTROLEE_STATE_PENDING_RUN) { + controlee->state = FIRA_CONTROLEE_STATE_RUNNING; + reset_rx_ctx = true; + } else if (controlee->state == FIRA_CONTROLEE_STATE_RUNNING) { + /* Stop raised by max number of measurements threshold. */ + if (session->stop_request) + controlee->state = + FIRA_CONTROLEE_STATE_STOPPING; + } else if (controlee->state == + FIRA_CONTROLEE_STATE_PENDING_STOP) { + controlee->state = FIRA_CONTROLEE_STATE_STOPPING; + } else if (controlee->state == + FIRA_CONTROLEE_STATE_PENDING_DEL) { + controlee->state = FIRA_CONTROLEE_STATE_DELETING; + } else if (controlee->state == FIRA_CONTROLEE_STATE_DELETING) { + list_del(&controlee->entry); + kfree(controlee); + session->n_current_controlees--; + reset_rx_ctx = true; + } + } - nb_blocks = session->block_index - session->last_block_index; - unsync_drift_dtu = (long long)nb_blocks * - session->params.block_duration_dtu * - FIRA_DRIFT_TOLERANCE_PPM / 1000000; - block_margin_dtu = - fira_session_get_block_duration_margin(local, session); - if (unsync_drift_dtu >= block_margin_dtu) - session->synchronised = false; + if (reset_rx_ctx && local->llhw->rx_ctx_size) { + for (i = 0; i < session->n_current_controlees; i++) { + memset(session->rx_ctx[i], 0, local->llhw->rx_ctx_size); + } + } } -struct fira_session *fira_session_next(struct fira_local *local, - u32 next_timestamp_dtu, - u32 max_access_duration_dtu) +bool fira_session_is_ready(const struct fira_local *local, + const struct fira_session *session) { - struct fira_session *selected_session; - u32 selected_timestamp_dtu = 0; - u32 selected_duration_dtu = 0; - u32 selected_end_dtu; + const struct fira_session_params *params = &session->params; + int round_duration_dtu; - if (list_empty(&local->active_sessions)) - return NULL; + if (params->multi_node_mode == FIRA_MULTI_NODE_MODE_UNICAST) { + if (session->n_current_controlees > 1) + return false; + } else { + /* On success, session will become active, so assume it is. */ + if (!fira_frame_check_n_controlees( + session, session->n_current_controlees, true)) + return false; + } - selected_session = fira_session_find_next(local, next_timestamp_dtu, - max_access_duration_dtu, - &selected_timestamp_dtu, - &selected_duration_dtu); - if (!selected_session) - return NULL; - selected_end_dtu = selected_timestamp_dtu + selected_duration_dtu + - local->llhw->anticip_dtu; - fira_session_send_collision_reports(local, selected_session, - selected_end_dtu); - selected_session->last_access_timestamp_dtu = selected_timestamp_dtu; - selected_session->last_access_duration_dtu = selected_duration_dtu; - return selected_session; -} + /* Check uwb parameters. */ + if (params->prf_mode == FIRA_PRF_MODE_BPRF) { + /* FIXME: when preamble code index is not set, we will use + * the default set one, that may be for HPRF... */ + if (params->preamble_code_index != 0 && + (params->preamble_code_index < 9 || + params->preamble_code_index > 24)) + return false; + if (params->sfd_id != FIRA_SFD_ID_0 && + params->sfd_id != FIRA_SFD_ID_2) + return false; + if (params->psdu_data_rate != FIRA_PSDU_DATA_RATE_6M81) + return false; + if (params->preamble_duration != FIRA_PREAMBULE_DURATION_64) + return false; + if (params->number_of_sts_segments > FIRA_STS_SEGMENTS_1) + return false; + } else { + if (params->preamble_code_index != 0 && + (params->preamble_code_index < 25 || + params->preamble_code_index > 32)) + return false; + if (params->sfd_id == FIRA_SFD_ID_0) + return false; + if (params->prf_mode == FIRA_PRF_MODE_HPRF && + params->psdu_data_rate > FIRA_PSDU_DATA_RATE_7M80) + return false; + if (params->prf_mode == FIRA_PRF_MODE_HPRF_HIGH_RATE && + params->psdu_data_rate < FIRA_PSDU_DATA_RATE_27M2) + return false; + } + if ((params->rframe_config == FIRA_RFRAME_CONFIG_SP0) && + (params->number_of_sts_segments != FIRA_STS_SEGMENTS_0)) + return false; + if ((params->rframe_config != FIRA_RFRAME_CONFIG_SP0) && + (params->number_of_sts_segments == FIRA_STS_SEGMENTS_0)) + return false; -void fira_session_resync(struct fira_session *session, u32 sts_index, - u32 timestamp_dtu) -{ - int block_duration_slots = session->params.block_duration_dtu / - session->params.slot_duration_dtu; - int slot_index = sts_index - session->crypto.sts_index_init; - int block_index = slot_index / block_duration_slots; - int round_slot_index = slot_index - block_index * block_duration_slots; - - session->block_index = block_index; - session->block_start_dtu = - timestamp_dtu - - round_slot_index * session->params.slot_duration_dtu; - session->sts_index = sts_index - round_slot_index; - session->round_index = - round_slot_index / session->params.round_duration_slots; - session->synchronised = true; - session->last_access_timestamp_dtu = timestamp_dtu; + round_duration_dtu = + params->slot_duration_dtu * params->round_duration_slots; + return params->slot_duration_dtu != 0 && + params->block_duration_dtu != 0 && + params->round_duration_slots != 0 && + round_duration_dtu <= params->block_duration_dtu; } -void fira_session_access_done(struct fira_local *local, - struct fira_session *session, - bool add_measurements) +/** + * proximity_enable_report() - Check proximity range to enable/disable report. + * @report_info: report info to be enabled/disabled + * @min_distance_mm: minimum distance in mm, value included + * @max_distance_mm: maximum distance in mm, value included + * @dtu_freq_hz: Frequency, to be used to compute distance from report + * @dtu_rctu: RCTU, to be used to compute distance from report + * + * Return: true if the report should be sent + * + * Report notification is sent with all of its measurements when: + * - it contains a stopped condition + * - it contains stopped controlees + * - it contains a measurement error + * - one of its measurement is inside of the configured proximity range + * + * Report notification is not sent when all of its measurements are valid + * and outside of the configured proximity range. + */ +static bool proximity_enable_report(const struct fira_report_info *report_info, + u32 min_distance_mm, u32 max_distance_mm, + int dtu_freq_hz, int dtu_rctu) { - if (session->controlee_management_flags == - FIRA_SESSION_CONTROLEE_MANAGEMENT_FLAG_STOP) { - fira_session_update_stopping_controlees(session); - session->controlee_management_flags = 0; - } - - if (session == local->current_session) { - if (!(session->params.device_type == - FIRA_DEVICE_TYPE_CONTROLEE && - local->ranging_info[0].status) && - !(session->params.device_type == - FIRA_DEVICE_TYPE_CONTROLLER && - local->n_ranging_valid != local->n_ranging_info)) { - session->last_block_index = session->block_index; - } else { - fira_session_check_unsync(local, session); + static const s64 speed_of_light_mm_per_s = 299702547000ull; + const s64 rctu_freq_hz = (s64)dtu_freq_hz * dtu_rctu; + s32 distance_mm; + const struct fira_ranging_info *ranging_data; + int i; + + if (report_info->stopped || report_info->n_stopped_controlees) { + return true; + } + + for (i = 0; i < report_info->n_ranging_data; i++) { + ranging_data = &report_info->ranging_data[i]; + if (ranging_data->status != FIRA_STATUS_RANGING_SUCCESS) { + return true; + } + if (!ranging_data->tof_present) { + return true; + } + /* Computation needs to be kept in sync with fira_session_report_measurement() */ + distance_mm = div64_s64(ranging_data->tof_rctu * + speed_of_light_mm_per_s, + rctu_freq_hz); + if (distance_mm >= min_distance_mm && + distance_mm <= max_distance_mm) { + return true; } - session->number_of_measurements++; - session->params.meas_seq.n_measurements_achieved++; } - fira_session_check_max_number_of_measurements(local, session); - fira_session_check_max_rr_retry(session); - fira_report(local, session, add_measurements); + return false; +} - if (session->controlee_management_flags & - FIRA_SESSION_CONTROLEE_MANAGEMENT_FLAG_UPDATE) { - fira_session_copy_controlees(&session->current_controlees, - &session->new_controlees); - session->controlee_management_flags &= - ~FIRA_SESSION_CONTROLEE_MANAGEMENT_FLAG_UPDATE; - } +void fira_session_report(struct fira_local *local, struct fira_session *session, + const struct fira_report_info *report_info) +{ + struct sk_buff *msg; + const struct fira_session_params *params = &session->params; - if (((session->stop_request || - session->max_number_of_measurements_reached) && - !(session->controlee_management_flags & - FIRA_SESSION_CONTROLEE_MANAGEMENT_FLAG_STOP)) || - session->stop_inband || session->stop_no_response) { - list_move(&session->entry, &local->inactive_sessions); - session->stop_request = false; - session->stop_inband = false; - session->stop_no_response = false; - session->max_number_of_measurements_reached = false; - /* Reset data parameters. */ - session->params.data_payload_seq = 0; - session->params.data_payload_len = 0; + if (params->range_data_ntf_config == FIRA_RANGE_DATA_NTF_DISABLED && + !report_info->stopped && !report_info->n_stopped_controlees) { + return; } - if (session == local->current_session) { - fira_session_stop_expired_sessions(local); - session->block_stride_len = session->next_block_stride_len; + if (params->range_data_ntf_config == FIRA_RANGE_DATA_NTF_PROXIMITY) { + if (!proximity_enable_report( + report_info, + params->range_data_ntf_proximity_near_mm, + params->range_data_ntf_proximity_far_mm, + local->llhw->dtu_freq_hz, local->llhw->dtu_rctu)) { + return; + } } + + trace_region_fira_session_report(session, report_info); + msg = mcps802154_region_event_alloc_skb(local->llhw, &local->region, + FIRA_CALL_SESSION_NOTIFICATION, + session->event_portid, + NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return; + + if (nla_put_u32(msg, FIRA_CALL_ATTR_SESSION_ID, session->id)) + goto nla_put_failure; + if (nla_put_u32(msg, FIRA_CALL_ATTR_SEQUENCE_NUMBER, + session->sequence_number)) + goto nla_put_failure; + if (fira_session_report_ranging_data(session, report_info, + local->llhw->dtu_freq_hz, + local->llhw->dtu_rctu, msg)) + goto nla_put_failure; + if (fira_session_report_ranging_diagnostics(session, report_info, msg)) + goto nla_put_failure; + session->sequence_number++; + session->data_payload.sent = false; + + skb_queue_tail(&local->report_queue, msg); + schedule_work(&local->report_work); + return; + +nla_put_failure: + kfree_skb(msg); } diff --git a/mac/fira_session.h b/mac/fira_session.h index 5f1a7cd..b23c8ab 100644 --- a/mac/fira_session.h +++ b/mac/fira_session.h @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2020-2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. @@ -24,20 +24,30 @@ #ifndef NET_MCPS802154_FIRA_SESSION_H #define NET_MCPS802154_FIRA_SESSION_H +#include "fira_session_fsm.h" #include "fira_region.h" #include "fira_crypto.h" #include "fira_round_hopping_crypto_impl.h" /** * enum fira_controlee_state - State of controlee. + * @FIRA_CONTROLEE_STATE_PENDING_RUN: The controlee will be set to running state + * at the end of round. * @FIRA_CONTROLEE_STATE_RUNNING: The controlee is running. - * @FIRA_CONTROLEE_STATE_PENDING_STOP: The controlee is stopping. - * @FIRA_CONTROLEE_STATE_PENDING_DEL: RFU. + * @FIRA_CONTROLEE_STATE_PENDING_STOP: The controlee will be set to stopping + * state at the end of round. + * @FIRA_CONTROLEE_STATE_STOPPING: The controlee is stopping. + * @FIRA_CONTROLEE_STATE_PENDING_DEL: The controlee will be set to deleting + * state at the end of round. + * @FIRA_CONTROLEE_STATE_DELETING: The controlee is being deleted. */ enum fira_controlee_state { + FIRA_CONTROLEE_STATE_PENDING_RUN, FIRA_CONTROLEE_STATE_RUNNING, FIRA_CONTROLEE_STATE_PENDING_STOP, + FIRA_CONTROLEE_STATE_STOPPING, FIRA_CONTROLEE_STATE_PENDING_DEL, + FIRA_CONTROLEE_STATE_DELETING, }; /** @@ -69,17 +79,10 @@ struct fira_controlee { * @state: Current state of the controlee. */ enum fira_controlee_state state; -}; - -enum fira_session_controlee_management_flags { - FIRA_SESSION_CONTROLEE_MANAGEMENT_FLAG_UPDATE = 1, - FIRA_SESSION_CONTROLEE_MANAGEMENT_FLAG_STOP = 2, -}; - -struct fira_controlees_array { - struct fira_controlee data[FIRA_CONTROLEES_MAX]; - /* Number of data valid. */ - size_t size; + /** + * @entry: Entry in list of controlees. + */ + struct list_head entry; }; struct fira_measurement_sequence_step { @@ -97,13 +100,6 @@ struct fira_measurement_sequence { size_t n_steps; }; -struct fira_measurement_sequence_data { - struct fira_measurement_sequence *active; - u8 current_step; - u8 n_measurements_achieved; - bool update_flag; -}; - struct fira_session_params { /* Main parameters. */ enum fira_device_type device_type; @@ -129,8 +125,11 @@ struct fira_session_params { enum fira_rframe_config rframe_config; enum fira_preambule_duration preamble_duration; enum fira_sfd_id sfd_id; + enum fira_sts_segments number_of_sts_segments; enum fira_psdu_data_rate psdu_data_rate; enum fira_mac_fcs_type mac_fcs_type; + enum fira_prf_mode prf_mode; + enum fira_phr_data_rate phr_data_rate; /* STS and crypto. */ enum fira_sts_config sts_config; u8 vupper64[FIRA_VUPPER64_SIZE]; @@ -139,13 +138,19 @@ struct fira_session_params { bool report_aoa_azimuth; bool report_aoa_elevation; bool report_aoa_fom; - struct fira_measurement_sequence_data meas_seq; - struct fira_measurement_sequence _meas_seq_1; - struct fira_measurement_sequence _meas_seq_2; + enum fira_rssi_report_type report_rssi; + struct fira_measurement_sequence meas_seq; u32 data_vendor_oui; u8 data_payload[FIRA_DATA_PAYLOAD_SIZE_MAX]; u32 data_payload_seq; int data_payload_len; + bool report_diagnostics; + enum fira_ranging_diagnostics_frame_report_flags diagnostic_report_flags; + /* Misc */ + enum fira_sts_length sts_length; + enum fira_range_data_ntf_config range_data_ntf_config; + u32 range_data_ntf_proximity_near_mm; + u32 range_data_ntf_proximity_far_mm; }; /** @@ -157,48 +162,78 @@ struct fira_session { */ u32 id; /** + * @sequence_number: Session notification counter. + */ + u32 sequence_number; + /** * @entry: Entry in list of sessions. */ struct list_head entry; /** + * @state: State of the session. + */ + const struct fira_session_fsm_state *state; + /** * @params: Session parameters, mostly read only while the session is * active. */ struct fira_session_params params; /** - * @block_start_dtu: Timestamp of the current or previous block. All - * other fields are referring to this same block. + * @hrp_uwb_params: HRP UWB parameters, read only while the session is + * active. */ - u32 block_start_dtu; + struct mcps802154_hrp_uwb_params hrp_uwb_params; /** - * @block_index: Block index of the reference block. + * @event_portid: Port identifier to use for notifications. */ - u32 block_index; + u32 event_portid; /** - * @sts_index: STS index value at reference block start. + * @block_start_valid: True when block_start_dtu is valid. + * It's false on the first access wo initiation delay. */ - u32 sts_index; + bool block_start_valid; /** - * @hopping_sequence_generation: Whether to compute round index from ranging round sequence. + * @block_start_dtu: Block start timestamp in dtu of the last + * get_access. */ - bool hopping_sequence_generation; + u32 block_start_dtu; /** - * @round_index: Round index of the reference block. + * @next_access_timestamp_dtu: Next access timestamp in dtu. + * It's equal to block_start_dtu when the hopping is disabled. + * Otherwise it's beyond the block_start_dtu. + * It's updated after each good or missed ranging round. */ - int round_index; + u32 next_access_timestamp_dtu; /** - * @next_round_index: Round index of the block after the reference block. + * @last_access_timestamp_dtu: Last timestamp where the session got + * the access. + * It's used only on session's election, when a negotiation between + * two session fails. */ - int next_round_index; + u32 last_access_timestamp_dtu; + /** + * @block_index: Block index used on the last access. + */ + u32 block_index; /** - * @block_stride_len: Stride length for the reference block. + * @block_stride_len: Stride length indicates how many ranging blocks + * will be skipped. + * The value is updated at the beginning of an access. */ int block_stride_len; /** - * @next_block_stride_len: Stride length for the block after the - * reference block. + * @round_index: Round index used on the last access. + */ + int round_index; + /** + * @next_round_index: Next round index a announced in measurement + * report message. */ - int next_block_stride_len; + int next_round_index; + /** + * @sts_index: STS index value on the last access. + */ + u32 sts_index; /** * @stop_request: Session has been requested to stop. */ @@ -214,10 +249,10 @@ struct fira_session { */ bool stop_no_response; /** - * @max_number_of_measurements_reached: Session has been requested to stop - * because max_number_of_measurements was reached. + * @n_ranging_round_retry: Number of ranging round failed. + * Counter reset on ranging round success. */ - bool max_number_of_measurements_reached; + int n_ranging_round_retry; /** * @crypto: Crypto context. */ @@ -227,49 +262,164 @@ struct fira_session { */ struct fira_round_hopping_sequence round_hopping_sequence; /** - * @event_portid: Port identifier to use for notifications. + * @controlee: Group of persistent variable(s) used when session + * is a controlee. + */ + struct { + /** + * @synchronised: Whether a controlee session was synchronised. + */ + bool synchronised; + /** + * @block_index_sync: Last block index received. + */ + int block_index_sync; + /** + * @hopping_mode: True when hopping is enabled on last + * measurement frame. + */ + bool hopping_mode; + /** + * @next_round_index_valid: True when the next round index + * is present in measurement report frame. + */ + bool next_round_index_valid; + } controlee; + /** + * @controller: Group of persistent variable(s) used when session + * is a controller. + */ + struct { + /** + * @next_block_index: Next block index built on get access with + * next round index. + * It's only to avoid to rebuild the next round index on next + * access, when this last occur in time as block index will + * match. + */ + u32 next_block_index; + } controller; + /** + * @data_payload: Local context for data_payload feature. + */ + struct { + /** + * @seq: Sequence number of last sent data. + */ + u32 seq; + /** + * @sent: True when data have been send during ranging round. + */ + bool sent; + } data_payload; + /** + * @current_controlees: Current list of controlees. + */ + struct list_head current_controlees; + /** + * @n_current_controlees: Number of elements in the list of current + * controlees. + */ + size_t n_current_controlees; + /** + * @measurements: Measurement configurations which influence diagnostics. + */ + struct { + /** + * @sequence: Copy of the meas_seq parameter on get_access + * event. + */ + struct fira_measurement_sequence sequence; + /** + * @index: Index of the step in sequence array. + */ + int index; + /** + * @n_achieved: Number of measurements done inside a step. + */ + int n_achieved; + /** + * @n_total_achieved: Total number of measurements done. + */ + int n_total_achieved; + /** + * @reset: True when new parameters have to be retrieved. + */ + bool reset; + } measurements; + /** + * @rx_ctx: Custom rx context for all controlees. + */ + void *rx_ctx[FIRA_CONTROLEES_MAX]; +}; + +/** + * struct fira_session_demand - Next access information for one FiRa session. + */ +struct fira_session_demand { + /** + * @block_start_dtu: Block start in dtu. */ - u32 event_portid; + u32 block_start_dtu; /** - * @synchronised: Whether a controlee session was synchronised. This - * field is not used for controller sessions. + * @timestamp_dtu: Access timestamp in dtu. */ - bool synchronised; + u32 timestamp_dtu; /** - * @last_access_timestamp_dtu: Timestamp of the last computed access. + * @max_duration_dtu: Maximum duration for the access. */ - u32 last_access_timestamp_dtu; + int max_duration_dtu; /** - * @last_access_duration_dtu: Duration of the last computed access. + * @add_blocks: Number of block to add. */ - u32 last_access_duration_dtu; + int add_blocks; /** - * @data_payload_seq_sent: Sequence number of last sent data. + * @round_index: Round index to apply for the access. */ - u32 data_payload_seq_sent; + int round_index; /** - * @last_block_index: Block index of the last successful ranging. + * @rx_timeout_dtu: timeout to apply when first frame of the controlee. */ - u32 last_block_index; + int rx_timeout_dtu; +}; + +/** + * struct fira_report_info - Report information for all peer. + */ +struct fira_report_info { + /** + * @ranging_data: Base address of ranging data per peer, or null + * pointer. + */ + const struct fira_ranging_info *ranging_data; + /** + * @n_ranging_data: Number of entry in ranging_data above. + */ + size_t n_ranging_data; /** - * @new_controlees: List of controlees to applies on next ca. + * @stopped_controlees: NULL, or short address of all stopped controlees. */ - struct fira_controlees_array new_controlees; + const __le16 *stopped_controlees; /** - * @current_controlees: List of controlees currently applied. + * @n_stopped_controlees: Number of controlees stopped in array above. */ - struct fira_controlees_array current_controlees; + size_t n_stopped_controlees; /** - * @controlee_management_flags: Flags used to indicates if the list of - * controlees must be updated and if any controlee must be stopped - * before allowing updates again. See - * &fira_session_controlee_management_flags. + * @diagnostics: Array of diagnostic collected per slots. */ - u32 controlee_management_flags; + const struct fira_diagnostic *diagnostics; /** - * @number_of_measurements: Number of measurements. + * @slots: Array of information slots. */ - u32 number_of_measurements; + const struct fira_slot *slots; + /** + * @n_slots: Number of slots above. + */ + size_t n_slots; + /** + * @stopped: True when the session is stopped. + */ + bool stopped; }; /** @@ -283,163 +433,87 @@ struct fira_session *fira_session_new(struct fira_local *local, u32 session_id); /** * fira_session_free() - Remove a session. - * @session: Session to remove, must be inactive. - */ -void fira_session_free(struct fira_session *session); - -/** - * fira_session_get() - Get a session by its identifier. * @local: FiRa context. - * @session_id: Session identifier. - * @active: When session is found set to true if active, false if inactive. - * - * Return: The session or NULL if not found. - */ -struct fira_session *fira_session_get(struct fira_local *local, u32 session_id, - bool *active); - -/** - * fira_session_copy_controlees() - copy controlees array between two array. - * @to: FiRa controlees array to write. - * @from: FiRa controlees array to read. + * @session: Session to remove, must be inactive. */ -void fira_session_copy_controlees(struct fira_controlees_array *to, - const struct fira_controlees_array *from); +void fira_session_free(struct fira_local *local, struct fira_session *session); /** * fira_session_set_controlees() - Set controlees. * @local: FiRa context. * @session: Session. - * @controlees_array: Destination array where to store the new controlees list. - * @controlees: Controlees information. + * @controlees: List of controlees. * @n_controlees: Number of controlees. * * Return: 0 or error. */ int fira_session_set_controlees(struct fira_local *local, struct fira_session *session, - struct fira_controlees_array *controlees_array, - const struct fira_controlee *controlees, - size_t n_controlees); + struct list_head *controlees, int n_controlees); /** * fira_session_new_controlees() - Add new controlees. * @session: Session. - * @active: True if session is active. - * @controlees_array: Destination array where to store the updated - * controlees list. - * @controlees: Controlees information. + * @controlees: List of controlees to add. * @n_controlees: Number of controlees. + * @async: True is the controlees must be added asynchronously. * * Return: 0 or error. */ -int fira_session_new_controlees(struct fira_session *session, bool active, - struct fira_controlees_array *controlees_array, - const struct fira_controlee *controlees, - size_t n_controlees); +int fira_session_new_controlees(struct fira_session *session, + struct list_head *controlees, int n_controlees, + bool async); /** - * fira_session_del_controlees() - Delete without stopping controlees. - * @controlees_array: Destination array where to store the updated - * controlees list. - * @controlees: Controlees information. - * @n_controlees: Number of controlees. + * fira_session_restart_controlees() - Restart controlee and erase pending del. + * @session: FiRa session context. * - * Return: 0 or error. + * Return: Number of controlee removed. */ -int fira_session_del_controlees(struct fira_controlees_array *controlees_array, - const struct fira_controlee *controlees, - size_t n_controlees); +void fira_session_restart_controlees(struct fira_session *session); /** - * fira_session_async_del_controlees() - Set flag to indicate that controlees - * need to be stopped then deleted. + * fira_session_del_controlees() - Delete controlees. * @session: Session. - * @controlees_array: Destination array where store new controlees list. - * @controlees: Controlees information. - * @n_controlees: Number of controlees. + * @controlees: List of controlees to delete. + * @async: True is the controlees must be deleted asynchronously. * * Return: 0 or error. */ -int fira_session_async_del_controlees( - struct fira_session *session, - struct fira_controlees_array *controlees_array, - const struct fira_controlee *controlees, size_t n_controlees); +int fira_session_del_controlees(struct fira_session *session, + struct list_head *controlees, bool async); /** * fira_session_stop_controlees() - Stop controlees. * @session: Session. - * @controlees_array: Destination array where store new controlees list. */ -void fira_session_stop_controlees( - struct fira_session *session, - struct fira_controlees_array *controlees_array); +void fira_session_stop_controlees(struct fira_session *session); /** - * fira_session_is_ready() - Test whether a session is ready to be started. - * @local: FiRa context. - * @session: Session to test. - * - * Return: true if the session can be started. - */ -bool fira_session_is_ready(struct fira_local *local, - struct fira_session *session); - -/** - * fira_session_prepare() - Prepare a FiRa session to run. + * fira_session_controlees_running_count() - Get the number of running controlees. * @session: Session. - */ -void fira_session_prepare(struct fira_session *session); - -/** - * fira_session_next() - Find the next session to use after the given timestamp. - * @local: FiRa context. - * @next_timestamp_dtu: Next access opportunity. - * @max_access_duration_dtu: Maximum access duration. * - * Return: The session or NULL if none. - */ -struct fira_session *fira_session_next(struct fira_local *local, - u32 next_timestamp_dtu, - u32 max_access_duration_dtu); - -/** - * fira_session_update_round_index() - Update round index for round hopping. - * @session: Session to update. + * Return: Number of running controlees. */ -void fira_session_update_round_index(struct fira_session *session); +int fira_session_controlees_running_count(const struct fira_session *session); /** - * fira_session_resync() - Resync session parameters on control message. - * @session: Session to synchronize. - * @sts_index: STS index of control message. - * @timestamp_dtu: Timestamp of control message. - */ -void fira_session_resync(struct fira_session *session, u32 sts_index, - u32 timestamp_dtu); - -/** - * fira_session_access_done() - Update session at end of access, or on event - * when no access is active. + * fira_session_update_controlees() - Update controlee's states. * @local: FiRa context. - * @session: Session. - * @add_measurements: True to add measurements to report. + * @session: Session to test. */ -void fira_session_access_done(struct fira_local *local, - struct fira_session *session, - bool add_measurements); +void fira_session_update_controlees(struct fira_local *local, + struct fira_session *session); /** - * fira_session_get_round_slot() - Get current round's slot. - * @session: Session. + * fira_session_is_ready() - Test whether a session is ready to be started. + * @local: FiRa context. + * @session: Session to test. * - * Return: The first slot of the current round. + * Return: true if the session can be started. */ -static inline u32 -fira_session_get_round_slot(const struct fira_session *session) -{ - return session->round_index * session->params.round_duration_slots; -} +bool fira_session_is_ready(const struct fira_local *local, + const struct fira_session *session); /** * fira_session_get_round_sts_index() - Get current round's STS index. @@ -450,57 +524,48 @@ fira_session_get_round_slot(const struct fira_session *session) static inline u32 fira_session_get_round_sts_index(const struct fira_session *session) { - return session->sts_index + fira_session_get_round_slot(session); -} + const struct fira_session_params *p = &session->params; -/** - * fira_session_get_block_duration_margin() - Get block duration margin. - * @local: FiRa context. - * @session: Session. - * - * Return: Block duration margin in dtu. - */ -static inline int -fira_session_get_block_duration_margin(struct fira_local *local, - const struct fira_session *session) -{ - return (long long int)session->params.block_duration_dtu * - (session->block_stride_len + 1) * - local->block_duration_rx_margin_ppm / 1000000; + return session->sts_index + + session->round_index * p->round_duration_slots; } /** - * fira_session_get_current_meas_seq_step() - Get current measurement step. + * fira_session_get_meas_seq_step() - Get current measurement step. * @session: Session. * * Return: Current Measurement Sequence step for given session. */ static inline const struct fira_measurement_sequence_step * -fira_session_get_current_meas_seq_step(const struct fira_session *session) +fira_session_get_meas_seq_step(const struct fira_session *session) { - return &(session->params.meas_seq.active - ->steps[session->params.meas_seq.current_step]); + const struct fira_measurement_sequence *seq = + &session->measurements.sequence; + + return &seq->steps[session->measurements.index]; } /** * fira_session_get_rx_ant_set() - Get Rx antenna set for a given message ID. - * @message_id: Message ID of Fira frame. * @session: Session. + * @message_id: Message ID of FiRa frame. * * Return: Adequate antenna set id for given frame and session parameters. */ -static inline s8 fira_session_get_rx_ant_set(const struct fira_session *session, - enum fira_message_id message_id) +static inline int +fira_session_get_rx_ant_set(const struct fira_session *session, + enum fira_message_id message_id) { + const struct fira_session_params *params = &session->params; const struct fira_measurement_sequence_step *step = - fira_session_get_current_meas_seq_step(session); + fira_session_get_meas_seq_step(session); if (message_id > FIRA_MESSAGE_ID_RFRAME_MAX) return step->rx_ant_set_nonranging; /* TODO: replace this test by device_role == FIRA_DEVICE_ROLE_INITIATOR * as soon as this feature is supported */ - if (session->params.device_type == FIRA_DEVICE_TYPE_CONTROLLER) + if (params->device_type == FIRA_DEVICE_TYPE_CONTROLLER) return step->rx_ant_sets_ranging[0]; else switch (step->type) { @@ -512,13 +577,38 @@ static inline s8 fira_session_get_rx_ant_set(const struct fira_session *session, case FIRA_MEASUREMENT_TYPE_AOA_AZIMUTH_ELEVATION: return step->rx_ant_sets_ranging [message_id == FIRA_MESSAGE_ID_RANGING_FINAL]; - /* LCOV_EXCL_START */ default: - /* defensive check, should not happen */ return -1; - /* LCOV_EXCL_STOP */ } return -1; } +/** + * fira_session_report() - Report state change and ranging result for a session. + * @local: FiRa context. + * @session: Session to report. + * @report_info: report information to exploit for the reporting. + */ +void fira_session_report(struct fira_local *local, struct fira_session *session, + const struct fira_report_info *report_info); + +/** + * fira_session_controlee_active() - Return whether the controlee is currently active. + * @controlee: Controlee. + * + * Return: True if the controlee is currently active. + */ +static inline bool +fira_session_controlee_active(struct fira_controlee *controlee) +{ + switch (controlee->state) { + case FIRA_CONTROLEE_STATE_RUNNING: + case FIRA_CONTROLEE_STATE_PENDING_STOP: + case FIRA_CONTROLEE_STATE_PENDING_DEL: + return true; + default: + return false; + } +} + #endif /* NET_MCPS802154_FIRA_SESSION_H */ diff --git a/mac/fira_session_fsm.c b/mac/fira_session_fsm.c new file mode 100644 index 0000000..3a9a086 --- /dev/null +++ b/mac/fira_session_fsm.c @@ -0,0 +1,157 @@ +/* + * This file is part of the UWB stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see <http://www.gnu.org/licenses/>. + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. Please contact Qorvo to inquire about licensing terms. + */ + +#include <linux/errno.h> + +#include "fira_session_fsm_init.h" +#include "fira_session_fsm_idle.h" +#include "fira_session_fsm_active.h" +#include "fira_session.h" +#include "fira_access.h" +#include "fira_trace.h" + +void fira_session_fsm_initialise(struct fira_local *local, + struct fira_session *session) +{ + list_add(&session->entry, &local->inactive_sessions); + session->state = &fira_session_fsm_init; + WARN_ON(!session->state->enter); + session->state->enter(local, session); +} + +void fira_session_fsm_uninit(struct fira_local *local, + struct fira_session *session) +{ + if (session->state->leave) + session->state->leave(local, session); + + trace_region_fira_session_fsm_change_state( + session, FIRA_SESSION_STATE_ID_DEINIT); + list_del(&session->entry); +} + +void fira_session_fsm_change_state( + struct fira_local *local, struct fira_session *session, + const struct fira_session_fsm_state *new_state) +{ + if (session->state->leave) + session->state->leave(local, session); + trace_region_fira_session_fsm_change_state(session, new_state->id); + session->state = new_state; + if (session->state->enter) + session->state->enter(local, session); +} + +bool fira_session_is_active(const struct fira_session *session) +{ + return session->state == &fira_session_fsm_active; +} + +enum fira_session_state_id +fira_session_get_state_id(const struct fira_session *session) +{ + return session->state->id; +} + +int fira_session_fsm_check_parameters(const struct fira_session *session, + struct nlattr **attrs) +{ + if (session->state->check_parameters) + return session->state->check_parameters(session, attrs); + return 0; +} + +void fira_session_fsm_parameters_updated(struct fira_local *local, + struct fira_session *session) +{ + /* The handler is defined for all states. */ + WARN_ON(!session->state->parameters_updated); + session->state->parameters_updated(local, session); +} + +void fira_session_fsm_controlee_list_updated(struct fira_local *local, + struct fira_session *session) +{ + if (session->state->controlee_list_updated) + session->state->controlee_list_updated(local, session); +} + +int fira_session_fsm_start(struct fira_local *local, + struct fira_session *session, + const struct genl_info *info) +{ + if (session->state->start) + return session->state->start(local, session, info); + return -EINVAL; +} + +int fira_session_fsm_stop(struct fira_local *local, + struct fira_session *session) +{ + if (session->state->stop) + return session->state->stop(local, session); + return 0; +} + +int fira_session_fsm_get_demand(const struct fira_local *local, + const struct fira_session *session, + u32 next_timestamp_dtu, int max_duration_dtu, + struct fira_session_demand *session_demand) +{ + /* + * fira_get_demand will not call this function without an + * active session. + */ + WARN_ON(!session->state->get_demand); + return session->state->get_demand(local, session, next_timestamp_dtu, + max_duration_dtu, session_demand); +} + +struct mcps802154_access * +fira_session_fsm_get_access(struct fira_local *local, + struct fira_session *session, + const struct fira_session_demand *session_demand) +{ + /* + * fira_get_access will not call this function without an + * active session. + */ + WARN_ON(!session->state->get_access); + return session->state->get_access(local, session, session_demand); +} + +void fira_session_fsm_access_done(struct fira_local *local, + struct fira_session *session, bool error) +{ + WARN_ON(!session->state->access_done); + return session->state->access_done(local, session, error); +} + +void fira_session_fsm_check_missed_ranging(struct fira_local *local, + struct fira_session *session, + u32 timestamp_dtu) +{ + WARN_ON(!session->state->check_missed_ranging); + return session->state->check_missed_ranging(local, session, + timestamp_dtu); +} diff --git a/mac/fira_session_fsm.h b/mac/fira_session_fsm.h new file mode 100644 index 0000000..9d1b622 --- /dev/null +++ b/mac/fira_session_fsm.h @@ -0,0 +1,241 @@ +/* + * This file is part of the UWB stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see <http://www.gnu.org/licenses/>. + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. Please contact Qorvo to inquire about licensing terms. + */ + +#ifndef NET_MCPS802154_FIRA_SESSION_FSM_H +#define NET_MCPS802154_FIRA_SESSION_FSM_H + +#include <linux/ieee802154.h> + +#include "fira_access.h" + +/* Forward declaration. */ +struct fira_local; +struct fira_session; +struct fira_session_demand; + +/** + * enum fira_session_state_id - State of the FiRa session. + * @FIRA_SESSION_STATE_ID_INIT: + * Initial state, session is not ready yet. + * @FIRA_SESSION_STATE_ID_DEINIT: + * Session does not exist. + * @FIRA_SESSION_STATE_ID_ACTIVE: + * Session is currently active. + * @FIRA_SESSION_STATE_ID_IDLE: + * Session is ready to start, but not currently active. + */ +enum fira_session_state_id { + FIRA_SESSION_STATE_ID_INIT, + FIRA_SESSION_STATE_ID_DEINIT, + FIRA_SESSION_STATE_ID_ACTIVE, + FIRA_SESSION_STATE_ID_IDLE, +}; + +/** + * struct fira_session_fsm_state - FiRa session FSM state. + * + * This structure contains the callbacks which are called on an event to handle + * the transition from the current state. + */ +struct fira_session_fsm_state { + /** @id: Name of state. */ + enum fira_session_state_id id; + /** @enter: Run when the state is entered. */ + void (*enter)(struct fira_local *local, struct fira_session *session); + /** @leave: Run when the state is left. */ + void (*leave)(struct fira_local *local, struct fira_session *session); + /** @check_parameters: Handle a check parameters. */ + int (*check_parameters)(const struct fira_session *session, + struct nlattr **attrs); + /** @parameters_updated: Handle parameters updated event. */ + void (*parameters_updated)(struct fira_local *local, + struct fira_session *session); + /** @controlee_list_updated: Handle controlee list updated event. */ + void (*controlee_list_updated)(struct fira_local *local, + struct fira_session *session); + /** @start: Handle start. */ + int (*start)(struct fira_local *local, struct fira_session *session, + const struct genl_info *info); + /** @stop: Handle stop. */ + int (*stop)(struct fira_local *local, struct fira_session *session); + /** @get_demand: Handle the get demand. */ + int (*get_demand)(const struct fira_local *local, + const struct fira_session *session, + u32 next_timestamp_dtu, int max_duration_dtu, + struct fira_session_demand *session_demand); + /** @get_access: Handle the get access. */ + struct mcps802154_access *(*get_access)( + struct fira_local *local, struct fira_session *session, + const struct fira_session_demand *session_demand); + /** @access_done: Handle end of access. */ + void (*access_done)(struct fira_local *local, + struct fira_session *session, bool error); + /** @check_missed_ranging: Handle the check of missed ranging. */ + void (*check_missed_ranging)(struct fira_local *local, + struct fira_session *session, + u32 timestamp_dtu); +}; + +/** + * fira_session_fsm_change_state() - Change the state of the FSM. + * @local: FiRa context. + * @session: Session context. + * @new_state: New to state to use in the FSM. + * + * This function shall be called only by fira_session_fsm files. + */ +void fira_session_fsm_change_state( + struct fira_local *local, struct fira_session *session, + const struct fira_session_fsm_state *new_state); + +/** + * fira_session_is_active() - Return the active status of the session. + * @session: Session context. + * + * Return: True is the session is active, false otherwise. + */ +bool fira_session_is_active(const struct fira_session *session); + +/** + * fira_session_fsm_initialise() - Initialize the FSM. + * @local: FiRa context. + * @session: Session context. + */ +void fira_session_fsm_initialise(struct fira_local *local, + struct fira_session *session); + +/** + * fira_session_fsm_uninit() - Uninitialise the FSM. + * @local: FiRa context. + * @session: Session context. + */ +void fira_session_fsm_uninit(struct fira_local *local, + struct fira_session *session); + +/** + * fira_session_get_state_id() - Get current state id (for reporting). + * @session: Session context. + * + * Return: State id value. + */ +enum fira_session_state_id +fira_session_get_state_id(const struct fira_session *session); + +/** + * fira_session_fsm_check_parameters() - Check parameters change ask by upper + * layer. + * @session: Session context. + * @attrs: Netlink attributs. + * + * Return: 0 on success, errno when change are refused. + */ +int fira_session_fsm_check_parameters(const struct fira_session *session, + struct nlattr **attrs); + +/** + * fira_session_fsm_parameters_updated() - Parameters updated by upper layer. + * @local: FiRa context. + * @session: Session context. + */ +void fira_session_fsm_parameters_updated(struct fira_local *local, + struct fira_session *session); + +/** + * fira_session_fsm_controlee_list_updated() - Controlee list updated by upper + * layer. + * @local: FiRa context. + * @session: Session context. + */ +void fira_session_fsm_controlee_list_updated(struct fira_local *local, + struct fira_session *session); + +/** + * fira_session_fsm_start() - Start request from upper layer. + * @local: FiRa context. + * @session: Session context. + * @info: Netlink info used only for the portid. + * + * Return: 0 on success, errno otherwise. + */ +int fira_session_fsm_start(struct fira_local *local, + struct fira_session *session, + const struct genl_info *info); + +/** + * fira_session_fsm_stop() - Stop request from upper layer. + * @local: FiRa context. + * @session: Session context. + * + * Return: 0 on success, errno otherwise. + */ +int fira_session_fsm_stop(struct fira_local *local, + struct fira_session *session); + +/** + * fira_session_fsm_get_demand() - Request the next ranging round of the session. + * @local: FiRa context. + * @session: Session context. + * @next_timestamp_dtu: Timestamp to start a demand. + * @max_duration_dtu: Max duration obligation to be consider by the session. + * @session_demand: Wish of the session when the return value is 1. + * + * Return: 1 for a session demand otherwise 0 for no demand. + */ +int fira_session_fsm_get_demand(const struct fira_local *local, + const struct fira_session *session, + u32 next_timestamp_dtu, int max_duration_dtu, + struct fira_session_demand *session_demand); + +/** + * fira_session_fsm_get_access() - Get access to process. + * @local: FiRa context. + * @session: Session context. + * @session_demand: Next access built by the get_demand. + * + * Return: The access for fproc, or NULL pointer. + */ +struct mcps802154_access * +fira_session_fsm_get_access(struct fira_local *local, + struct fira_session *session, + const struct fira_session_demand *session_demand); + +/** + * fira_session_fsm_access_done() - End of the access to report. + * @local: FiRa context. + * @session: Session context. + * @error: True when an error happen. + */ +void fira_session_fsm_access_done(struct fira_local *local, + struct fira_session *session, bool error); + +/** + * fira_session_fsm_check_missed_ranging() - Report a missed ranging if exist. + * @local: FiRa context. + * @session: Session context. + * @timestamp_dtu: Timestamp dtu where no fallback is possible. + */ +void fira_session_fsm_check_missed_ranging(struct fira_local *local, + struct fira_session *session, + u32 timestamp_dtu); + +#endif /* NET_MCPS802154_FIRA_SESSION_FSM_H */ diff --git a/mac/fira_session_fsm_active.c b/mac/fira_session_fsm_active.c new file mode 100644 index 0000000..75a1c1b --- /dev/null +++ b/mac/fira_session_fsm_active.c @@ -0,0 +1,983 @@ +/* + * This file is part of the UWB stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see <http://www.gnu.org/licenses/>. + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. Please contact Qorvo to inquire about licensing terms. + */ + +#include <net/mcps802154_frame.h> +#include <net/fira_region_nl.h> +#include <linux/errno.h> +#include <linux/math64.h> + +#include "fira_round_hopping_sequence.h" +#include "fira_session_fsm_init.h" +#include "fira_session_fsm_idle.h" +#include "fira_session_fsm_active.h" +#include "fira_session.h" +#include "fira_trace.h" +#include "warn_return.h" + +/** + * list_move_to_active() - Move from inactive list to active list. + * @local: FiRa context. + * @session: Session context. + */ +static void list_move_to_active(struct fira_local *local, + struct fira_session *session) +{ + struct list_head *position = &local->active_sessions; + struct fira_session *tmp; + + /* + * Search the position to maintain a list sorted from highest to + * lowest priority. And for the same priority keep the call + * order (moved to the tail). + * Highest value of priority is the highest priority. + * Range of priority is between: 0 to FIRA_PRIORITY_MAX. + */ + list_for_each_entry (tmp, &local->active_sessions, entry) { + if (session->params.priority <= tmp->params.priority) + position = &tmp->entry; + else + break; + } + list_move(&session->entry, position); +} + +/** + * get_channel() - Retrieve the channel to applied on the access. + * @local: FiRa context. + * @session: Session context. + * + * Return: The channel. + */ +static const struct mcps802154_channel * +get_channel(struct fira_local *local, const struct fira_session *session) +{ + const struct fira_session_params *params = &session->params; + + if (params->channel_number || params->preamble_code_index) { + const struct mcps802154_channel *channel = + mcps802154_get_current_channel(local->llhw); + + local->channel = *channel; + if (params->channel_number) + local->channel.channel = params->channel_number; + if (params->preamble_code_index) + local->channel.preamble_code = + params->preamble_code_index; + return &local->channel; + } + return NULL; +} + +/** + * get_round_index() - Return the round index for a specific block index. + * @session: Session context. + * @block_index: Block index. + * + * Return: Round index freshly computed or the round index saved. + */ +static int get_round_index(const struct fira_session *session, int block_index) +{ + const struct fira_session_params *params = &session->params; + int expected_block_index; + + switch (params->device_type) { + default: + case FIRA_DEVICE_TYPE_CONTROLLER: + if (!params->round_hopping) + return 0; + /* + * Avoid to rebuild the round_index. + * The condition is true on first get_access too. + */ + if (session->controller.next_block_index == block_index) + return session->next_round_index; + break; + case FIRA_DEVICE_TYPE_CONTROLEE: + if (!session->controlee.hopping_mode) + return 0; + /* + * Return the round index received, only when the block index + * match with expected block index. + */ + expected_block_index = session->controlee.block_index_sync + + session->block_stride_len + 1; + if (expected_block_index == block_index && + session->controlee.next_round_index_valid) + return session->next_round_index; + break; + } + return fira_round_hopping_sequence_get(session, block_index); +} + +/** + * get_rx_margin_duration_dtu() - Build the maximum margin tolerance for Rx. + * @local: FiRa context. + * @session: Session context. + * + * Return: Duration to apply on first and Rx frame of controlee's access. + */ +static int get_rx_margin_duration_dtu(const struct fira_local *local, + const struct fira_session *session) +{ + const struct fira_session_params *params = &session->params; + s64 duration_dtu = (s64)(session->block_stride_len + 1) * + params->block_duration_dtu; + /* + * TODO: Unit test should be able to predic timestamp. + * - Replace 'local->block_duration_rx_margin_ppm by' + * UWB_BLOCK_DURATION_MARGIN_PPM + * - Remove 'local' from args. + */ + return div64_s64(duration_dtu * local->block_duration_rx_margin_ppm, + 1000000); +} + +/** + * get_next_access_timestamp_dtu() - Build the next access timestamp. + * @local: FiRa context. + * @session: Session context. + * + * Return: Timestamp in dtu. + */ +static u32 get_next_access_timestamp_dtu(const struct fira_local *local, + const struct fira_session *session) +{ + const struct fira_session_params *params = &session->params; + int add_blocks = session->block_stride_len + 1; + int next_block_index = session->block_index + add_blocks; + int next_round_index = get_round_index(session, next_block_index); + int round_duration_dtu = + params->round_duration_slots * params->slot_duration_dtu; + u32 next_block_start_dtu = session->block_start_dtu + + add_blocks * params->block_duration_dtu + + next_round_index * round_duration_dtu; + + switch (params->device_type) { + default: + case FIRA_DEVICE_TYPE_CONTROLLER: + return next_block_start_dtu; + case FIRA_DEVICE_TYPE_CONTROLEE: + return next_block_start_dtu - + get_rx_margin_duration_dtu(local, session); + } +} + +/** + * is_controlee_synchronised() - Answer to the question of the synchronisation + * status. + * @local: FiRa context. + * @session: Session context. + * + * Return: True when the controlee session is still synchronized. + */ +static bool is_controlee_synchronised(const struct fira_local *local, + const struct fira_session *session) +{ +#define FIRA_DRIFT_TOLERANCE_PPM 30 + const struct fira_session_params *params = &session->params; + int n_unsync_blocks; + s64 unsync_duration_dtu; + int drift_ppm, rx_margin_ppm; + + if (session->controlee.synchronised) { + n_unsync_blocks = session->block_index - + session->controlee.block_index_sync; + unsync_duration_dtu = + n_unsync_blocks * params->block_duration_dtu; + drift_ppm = div64_s64(unsync_duration_dtu * + FIRA_DRIFT_TOLERANCE_PPM, + 1000000); + rx_margin_ppm = get_rx_margin_duration_dtu(local, session); + + trace_region_fira_is_controlee_synchronised(session, drift_ppm, + rx_margin_ppm); + if (drift_ppm <= rx_margin_ppm) + return true; + } + return false; +} + +/** + * is_stopped() - Is the session stopped? + * @session: Session context. + * + * Return: True when the session is stopped, false otherwise. + */ +static bool is_stopped(struct fira_session *session) +{ + const struct fira_session_params *params = &session->params; + int nb_controlee = fira_session_controlees_running_count(session); + + if (params->max_rr_retry && + session->n_ranging_round_retry >= params->max_rr_retry) + session->stop_no_response = true; + + return (session->stop_request && !nb_controlee) || + session->stop_inband || session->stop_no_response; +} + +/** + * forward_to_next_ranging() - Update the session to forward to next ranging. + * @session: Session context. + * @n_ranging: Number of ranging (forward). + */ +static void forward_to_next_ranging(struct fira_session *session, int n_ranging) +{ + const struct fira_session_params *params = &session->params; + int blocks_per_ranging = session->block_stride_len + 1; + int add_blocks = n_ranging * blocks_per_ranging; + int duration_dtu = add_blocks * params->block_duration_dtu; + int slots_per_block = + params->block_duration_dtu / params->slot_duration_dtu; + + session->block_index += add_blocks; + session->block_start_dtu += duration_dtu; + session->sts_index += add_blocks * slots_per_block; + session->n_ranging_round_retry += n_ranging; +} + +/** + * ranging_round_done() - Update controlee and notify the upper layer. + * @local: FiRa context. + * @session: Session context. + * @report_info: Report information to forward fira_session_report. + */ +static void ranging_round_done(struct fira_local *local, + struct fira_session *session, + struct fira_report_info *report_info) +{ + const struct fira_session_params *params = &session->params; + + session->next_access_timestamp_dtu = + get_next_access_timestamp_dtu(local, session); + report_info->stopped = is_stopped(session); + + switch (params->device_type) { + default: + case FIRA_DEVICE_TYPE_CONTROLLER: + /* Update controlee's states between two ranging round. */ + fira_session_update_controlees(local, session); + break; + case FIRA_DEVICE_TYPE_CONTROLEE: + /* Did the controlee's access lose the synchronisation? */ + session->controlee.synchronised = + is_controlee_synchronised(local, session); + break; + } + + fira_session_report(local, session, report_info); + + if (report_info->stopped) + fira_session_fsm_change_state(local, session, + &fira_session_fsm_idle); + else + forward_to_next_ranging(session, 1); +} + +static void fira_session_fsm_active_enter(struct fira_local *local, + struct fira_session *session) +{ + const struct fira_session_params *params = &session->params; + + session->stop_request = false; + session->stop_inband = false; + session->stop_no_response = false; + session->measurements.n_total_achieved = 0; + session->block_stride_len = params->block_stride_len; + session->controlee.synchronised = false; + session->controlee.hopping_mode = false; + session->controlee.next_round_index_valid = false; + session->controlee.block_index_sync = 0; + session->round_index = 0; + /* + * Initialize to 1 when initiation_time_ms is 0, + * because first add_blocks built will be 0. + */ + session->n_ranging_round_retry = params->initiation_time_ms ? 0 : 1; + + list_move_to_active(local, session); +} + +static void fira_session_fsm_active_leave(struct fira_local *local, + struct fira_session *session) +{ + list_move(&session->entry, &local->inactive_sessions); + fira_session_restart_controlees(session); +} + +static int +fira_session_fsm_active_check_parameters(const struct fira_session *session, + struct nlattr **attrs) +{ + const struct fira_session_params *params = &session->params; + enum fira_session_param_attrs i; + + for (i = FIRA_SESSION_PARAM_ATTR_UNSPEC + 1; + i <= FIRA_SESSION_PARAM_ATTR_MAX; i++) { + const struct nlattr *attr = attrs[i]; + + if (!attr) + /* Attribute not provided. */ + continue; + + switch (i) { + case FIRA_SESSION_PARAM_ATTR_MEASUREMENT_SEQUENCE: + case FIRA_SESSION_PARAM_ATTR_DATA_PAYLOAD: + case FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_CONFIG: + case FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_PROXIMITY_NEAR: + case FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_PROXIMITY_FAR: + /* Allowed for all device type. */ + break; + case FIRA_SESSION_PARAM_ATTR_BLOCK_STRIDE_LENGTH: + /* Allowed only for controller. */ + if (params->device_type == FIRA_DEVICE_TYPE_CONTROLLER) + continue; + return -EBUSY; + default: + return -EBUSY; + } + } + return 0; +} + +static void +fira_session_fsm_active_parameters_updated(struct fira_local *local, + struct fira_session *session) +{ + const struct fira_session_params *params = &session->params; + int i; + + if (session->measurements.reset) { + for (i = 0; i < params->meas_seq.n_steps; i++) { + const struct fira_measurement_sequence_step *step; + + step = ¶ms->meas_seq.steps[i]; + trace_region_fira_session_meas_seq_params(session, step, + i); + } + } +} + +static int fira_session_fsm_active_start(struct fira_local *local, + struct fira_session *session, + const struct genl_info *info) +{ + /* Already started. */ + return 0; +} + +static int fira_session_fsm_active_stop(struct fira_local *local, + struct fira_session *session) +{ + const struct fira_session_params *params = &session->params; + struct fira_report_info report_info = { + .stopped = true, + }; + + switch (params->device_type) { + default: + case FIRA_DEVICE_TYPE_CONTROLLER: + if (local->current_session == NULL) { + session->stop_request = true; + /* + * FIXME/BUG: + * In unit test, the stop_tx_frame_error (or rx), + * stop the current access and trig a broken event. + * Then the TearDown request a session_stop, but + * there is still more than one controlee running. + * + * Normally the controller must do an access to + * announce a stop of all controlees. + * But here, there is a missing mechanism, as: + * - notify_stop is not called, + * - error is only a boolean in access_done. + * And the error boolean is True on -ETIME. + * + * And as the current_session equal to NULL is a + * normal behavior in multi-region. We have a bug. + */ + fira_session_report(local, session, &report_info); + fira_session_fsm_change_state(local, session, + &fira_session_fsm_idle); + } else if (session->n_current_controlees) { + /* + * A ranging round to announced all controlee + * stopped is required. + */ + fira_session_stop_controlees(session); + session->stop_request = true; + } else if (local->current_session != session) { + session->stop_request = true; + fira_session_report(local, session, &report_info); + fira_session_fsm_change_state(local, session, + &fira_session_fsm_idle); + } + break; + case FIRA_DEVICE_TYPE_CONTROLEE: + session->stop_request = true; + if (local->current_session != session) { + fira_session_report(local, session, &report_info); + fira_session_fsm_change_state(local, session, + &fira_session_fsm_idle); + } + break; + } + mcps802154_reschedule(local->llhw); + return 0; +} + +static int +fira_session_fsm_active_get_demand(const struct fira_local *local, + const struct fira_session *session, + u32 next_timestamp_dtu, int max_duration_dtu, + struct fira_session_demand *session_demand) +{ + const struct fira_session_params *params = &session->params; + int round_duration_dtu = + params->round_duration_slots * params->slot_duration_dtu; + u32 block_start_dtu; + u32 timestamp_dtu; + u32 duration_dtu; + int round_index = 0; + u32 block_index; + int add_blocks = 0; + int rx_timeout_dtu = 0; + int slot_count; + + /* First, determine two dates: block_start_dtu and timestamp_dtu. */ + if (!is_before_dtu(session->next_access_timestamp_dtu, + next_timestamp_dtu)) { + /* + * block_start_dtu is set in the future or present. + * It's happen mainly when initiation_time_ms is not zero. + */ + timestamp_dtu = block_start_dtu = session->block_start_dtu; + } else { + /* + * block start is in the past, we have to evaluate the + * new block start dtu. + * It's could be the same with a controlee not synchronized. + * + * Example of time graph of what's could happen: + * + * -------x----------------x----------------x------- + * #x - 1 | Block Index #x | #x + 1 | #x + 2 + * -------x----------------x------x---------x-------> Time + * |<--------------------->| + * Block | | + * start | next_timestamp_dtu + * | + * duration_from_block_start_dtu + * + * In the graph example, one block is missed, but it's could be + * more or less(controlee). + */ + int duration_from_block_start_dtu = + next_timestamp_dtu - session->block_start_dtu; + + if (params->device_type == FIRA_DEVICE_TYPE_CONTROLEE && + !session->controlee.synchronised) { + /* + * With a controlee not synchronized, consider the + * block as missed when there is no more left duration + * in the current block. + * + * block + * start next_timestamp_dtu + * | | + * -------x-----------------x---x------ + * #x - 1 | #x : | #x + 1 + * -------x-----------------x---x-----> Time + * | | + * |<--------------->| + * | + * | + * add_blocks = Time / block_duration + */ + add_blocks = duration_from_block_start_dtu / + params->block_duration_dtu; + } else { + int blocks_per_ranging = session->block_stride_len + 1; + + /* + * With a controller or a controlee synchronized, + * consider a block started as a missed block. + */ + add_blocks = (duration_from_block_start_dtu + + params->block_duration_dtu - 1) / + params->block_duration_dtu; + /* + * Block stride feature announced/received in last + * access. + */ + if (session->block_stride_len) { + int n = add_blocks % blocks_per_ranging; + + /* + * Add more block(s) to reach block stride + * modulo. + */ + if (n) + add_blocks += blocks_per_ranging - n; + } + } + + /* Compute block start dtu. 'add_blocks' can be zero. */ + block_start_dtu = session->block_start_dtu + + add_blocks * params->block_duration_dtu; + /* Determine the access timestamp. */ + if (is_before_dtu(block_start_dtu, next_timestamp_dtu)) + /* + * Only the controlee not synchronized can have its + * next access timestamp_dtu in the future of the + * block start. + * + * block_start_dtu + * | + * -------x-----------------x---------- + * #x - 1 | Block index #x | #x + 1 + * -------x------x----------x----------> Time + * | + * next_timestamp_dtu + */ + timestamp_dtu = next_timestamp_dtu; + else + timestamp_dtu = block_start_dtu; + } + + /* + * As block_start_dtu is updated with new timestamp in the future, + * or still in the past (controlee), then other variables will be + * build to fill the session_demand output. + * + * In other words, locale variables can have a new values which + * represent the next(future) block/access/index/... + * Or keep +/- the same values. + */ + block_index = session->block_index + add_blocks; + switch (params->device_type) { + default: + case FIRA_DEVICE_TYPE_CONTROLLER: + slot_count = fira_session_get_slot_count(session); + round_index = get_round_index(session, block_index); + timestamp_dtu = + block_start_dtu + round_index * round_duration_dtu; + duration_dtu = slot_count * params->slot_duration_dtu; + if (max_duration_dtu && + is_before_dtu(next_timestamp_dtu + max_duration_dtu, + timestamp_dtu + duration_dtu)) + /* No way to start an access. */ + return 0; + break; + case FIRA_DEVICE_TYPE_CONTROLEE: + if (session->controlee.synchronised) { + int margin_less, margin_more; + + /* + * Time graph to illustrate the controlee access + * and its synchronization. + * + * Next Timestamp + * timestamp without margin + * | | + * ----x--------x----x----x-----> Time + * |<---|--->| + * Rx enabled Rx timeout + * @timestamp_dtu + * + * rx_margin is the maximum margin accepted. + */ + round_index = get_round_index(session, block_index); + timestamp_dtu += round_index * round_duration_dtu; + margin_less = margin_more = + get_rx_margin_duration_dtu(local, session); + if (timestamp_dtu - next_timestamp_dtu < margin_less) + /* + * Avoid to build a timestamp_dtu which is in + * the past of next_timestamp_dtu. + */ + margin_less = + next_timestamp_dtu - timestamp_dtu; + timestamp_dtu -= margin_less; + rx_timeout_dtu = margin_less + margin_more; + duration_dtu = round_duration_dtu + margin_less; + if (max_duration_dtu && + is_before_dtu(next_timestamp_dtu + max_duration_dtu, + timestamp_dtu + duration_dtu)) + /* No way to start an access. */ + return 0; + } else { + int unsync_max_duration_dtu = + params->block_duration_dtu + + params->slot_duration_dtu; + + /* + * A controlee not synchronized is allowed to start/end + * anywhere in the block to find the controller. + * But the session continue to work with block duration + * to provide: + * - Regular reporting. + * - Time-sharing in multi-session/multi-region. + * + * Time graph: + * + * unsync_max_duration_dtu + * |<----------------------------->| + * | | + * --+---x-----------------------|-------x------> + * | Block #x | Block #x + 1 + * --+---x-----------------------|---x---x------> Time + * |<------------------------->|<->| + * block duration slot duration + * + * The unsync duration is bigger than the block, to + * listen the medium for one block min. But to avoid + * to be in late on the next access, we must add one + * slot. + */ + if (max_duration_dtu && + is_before_dtu(next_timestamp_dtu + max_duration_dtu, + timestamp_dtu + + params->slot_duration_dtu)) + /* No way to start an access. */ + return 0; + else if (!max_duration_dtu || + is_before_dtu(timestamp_dtu + + unsync_max_duration_dtu, + next_timestamp_dtu + + max_duration_dtu)) + /* Maximum access granted. */ + duration_dtu = unsync_max_duration_dtu; + else + /* Adjusted access duration. */ + duration_dtu = next_timestamp_dtu + + max_duration_dtu - timestamp_dtu; + + /* + * 'rx_timeout_dtu' is set to allow the reception + * of the control frame close to the end of the + * access, and so be synchronized for next block. + * + * But if the control message is received + * at the end of access, the other frames + * will be dropped to respect the duration_dtu. + * See: rx control frame. + */ + rx_timeout_dtu = + duration_dtu - params->slot_duration_dtu; + } + break; + } + + /* + * Update the session demand (output): + * - rx_timeout_dtu: Used only by the controlee. + * + * On the get_access, the session_demand will be applied + * to the session. Otherwise the session_demand is dropped. + * + * In a way, session_demand represent the next access. + */ + *session_demand = (struct fira_session_demand){ + .block_start_dtu = block_start_dtu, + .timestamp_dtu = timestamp_dtu, + .max_duration_dtu = duration_dtu, + .add_blocks = add_blocks, + .rx_timeout_dtu = rx_timeout_dtu, + .round_index = round_index, + }; + trace_region_fira_session_fsm_active_get_demand_return(local, session, + session_demand); + return 1; +} + +static struct mcps802154_access * +fira_session_fsm_active_get_access(struct fira_local *local, + struct fira_session *session, + const struct fira_session_demand *fsd) +{ + const struct fira_session_params *params = &session->params; + const struct mcps802154_hrp_uwb_params *hrp = &session->hrp_uwb_params; + struct mcps802154_access *access = &local->access; + int blocks_per_ranging; + + /* + * , , + * (\____/) Important: + * (_oo_) + * (O) It's almost forbidden to update session + * __||__ \) content for a controlee. + * []/______\[] / + * / \______/ \/ Because, the session can change on control + * / /__\ frame reception (static STS only). + * (\ /____\ + */ + local->current_session = session; + + /* + * Update common access fields for controlee and controller. + * hrp must stay const, see 'Important' above. + */ + access->method = MCPS802154_ACCESS_METHOD_MULTI; + access->frames = local->frames; + access->n_frames = 0; + access->channel = get_channel(local, session); + access->hrp_uwb_params = hrp; + + /* + * For the ranging round failure counter, consider these rounds as + * failed. And reset the counter in the access_done if success. + */ + blocks_per_ranging = session->block_stride_len + 1; + session->n_ranging_round_retry += fsd->add_blocks / blocks_per_ranging; + + /* Continue to 'device type' access. */ + if (params->device_type == FIRA_DEVICE_TYPE_CONTROLLER) + return fira_get_access_controller(local, fsd); + return fira_get_access_controlee(local, fsd); +} + +static void fira_session_fsm_active_access_done(struct fira_local *local, + struct fira_session *session, + bool error) +{ + const struct fira_session_params *params = &session->params; + const struct fira_measurement_sequence_step *step; + struct fira_report_info report_info = { + .ranging_data = local->ranging_info, + .n_ranging_data = local->n_ranging_info, + .stopped_controlees = local->stopped_controlees, + .n_stopped_controlees = local->n_stopped_controlees, + .diagnostics = local->diagnostics, + .slots = local->slots, + .n_slots = local->access.n_frames, + }; + struct fira_ranging_info *ri; + int i; + + /* Update local. */ + local->current_session = NULL; + + if (error) { + /* + * FIXME: + * Why corrupt all status, the last slot_index is not + * enough? + * TODO: Proposal: + * - Set INTERNAL_ERROR on status during the get_access. + * - Update status on tx_return/rx_frame. + * - Update testu which expect the wrong status. + */ + for (i = 0; i < local->n_ranging_info; i++) { + ri = &local->ranging_info[i]; + ri->status = FIRA_STATUS_RANGING_INTERNAL_ERROR; + } + } else { + for (i = 0; i < local->n_ranging_info; i++) { + ri = &local->ranging_info[i]; + if (ri->status != FIRA_STATUS_RANGING_SUCCESS) + break; + } + /* Reset ranging round failure counter. */ + if (i == local->n_ranging_info) + session->n_ranging_round_retry = 0; + } + + session->measurements.n_achieved++; + session->measurements.n_total_achieved++; + step = fira_session_get_meas_seq_step(session); + if (session->measurements.reset) { + /* Copy new measurement sequence. */ + session->measurements.sequence = params->meas_seq; + session->measurements.index = 0; + session->measurements.n_achieved = 0; + session->measurements.reset = false; + } else if (session->measurements.n_achieved >= step->n_measurements) { + struct fira_measurement_sequence *seq = + &session->measurements.sequence; + + session->measurements.n_achieved = 0; + session->measurements.index++; + session->measurements.index %= seq->n_steps; + } + /* Check max number of measurements. */ + if (params->max_number_of_measurements && + session->measurements.n_total_achieved >= + params->max_number_of_measurements) { + session->stop_request = true; + } + + ranging_round_done(local, session, &report_info); +} + +static void +fira_session_fsm_active_check_missed_ranging(struct fira_local *local, + struct fira_session *session, + u32 timestamp_dtu) +{ + const struct fira_session_params *params = &session->params; + int next_block_start_dtu = + session->block_start_dtu + params->block_duration_dtu; + bool is_missed_ranging_round = false; + + /* + * Example of possible timings (without hopping): + * + * check(timestamp_dtu) + * Ok Miss Miss | + * Session: [--] [--] [--] | [--] + * ------x---------x---------------x--------> Time + * | | + * block_start_dtu next_access_timestamp_dtu + * + * Tips: + * - 'session->block_start_dtu' is the block start of the last access. + * - 'session->next_access_timestamp_dtu' value can be: + * - Next block start when hopping is disabled. + * - Beyond the next block start when hopping is enabled. + * - When the session haven't been check since a long time, + * many blocks could be missed. + */ + + /* First, determine if there is missed ranging round. */ + if (params->device_type == FIRA_DEVICE_TYPE_CONTROLEE && + !session->controlee.synchronised) { + /* Consider the block as missed when next block is reached. */ + if (!is_before_dtu(timestamp_dtu, next_block_start_dtu)) + is_missed_ranging_round = true; + } else if (is_before_dtu(session->next_access_timestamp_dtu, + timestamp_dtu)) { + /* A late is not accepted here. */ + is_missed_ranging_round = true; + } + + /* Compute the number of missed ranging. */ + if (is_missed_ranging_round) { + int blocks_per_ranging = session->block_stride_len + 1; + int add_blocks = 0; + + /* Drift probably due to multi-session or multi-region. */ + if (is_before_dtu(next_block_start_dtu, timestamp_dtu)) + add_blocks = (timestamp_dtu - next_block_start_dtu) / + params->block_duration_dtu; + if (add_blocks >= blocks_per_ranging) { + int n_ranging_failed = add_blocks / blocks_per_ranging; + + if (params->max_rr_retry && + session->n_ranging_round_retry + n_ranging_failed > + params->max_rr_retry) { + /* + * Avoid to set a block index bigger than the + * max ranging round retry in the report. + */ + n_ranging_failed = + params->max_rr_retry - + session->n_ranging_round_retry; + } + forward_to_next_ranging(session, n_ranging_failed); + } + } + + /* Finally, do the missed ranging round report. */ + if (is_missed_ranging_round) { + struct fira_report_info report_info = {}; + __le16 *pend_del; + struct fira_ranging_info *ri; + int j, k; + struct fira_controlee *controlee; + + /* + * \\\||||||//// + * \\ ~ ~ // + * ( @ @ ) + * _________ oOOo-(_)-oOOo________________________________ + * WARN_RETURN_VOID_ON: Because the 'local' information will + * be used until the end of this bloc. + * So this function must not be called during an access, + * to avoid to use a shared memory already used by current + * session. + * ________________Oooo.__________________________________ + * .oooO ( ) + * ( ) ) / + * \ ( (_/ + * \_) + */ + WARN_RETURN_VOID_ON(local->current_session); + /* Build a missed ranging round report. */ + report_info.ranging_data = local->ranging_info; + switch (params->device_type) { + default: + case FIRA_DEVICE_TYPE_CONTROLLER: + pend_del = local->stopped_controlees; + j = k = 0; + list_for_each_entry (controlee, + &session->current_controlees, + entry) { + switch (controlee->state) { + case FIRA_CONTROLEE_STATE_RUNNING: + case FIRA_CONTROLEE_STATE_PENDING_STOP: + case FIRA_CONTROLEE_STATE_PENDING_DEL: + ri = &local->ranging_info[j]; + *ri = (struct fira_ranging_info){ + .short_addr = + controlee->short_addr, + .status = + FIRA_STATUS_RANGING_TX_FAILED, + }; + j++; + break; + default: + pend_del[k++] = controlee->short_addr; + break; + } + } + report_info.stopped_controlees = pend_del; + report_info.n_stopped_controlees = k, + report_info.n_ranging_data = j; + break; + case FIRA_DEVICE_TYPE_CONTROLEE: + ri = &local->ranging_info[0]; + *ri = (struct fira_ranging_info){ + .short_addr = params->controller_short_addr, + .status = FIRA_STATUS_RANGING_RX_TIMEOUT, + }; + report_info.n_ranging_data = 1; + break; + } + ranging_round_done(local, session, &report_info); + } +} + +const struct fira_session_fsm_state fira_session_fsm_active = { + .id = FIRA_SESSION_STATE_ID_ACTIVE, + .enter = fira_session_fsm_active_enter, + .leave = fira_session_fsm_active_leave, + .check_parameters = fira_session_fsm_active_check_parameters, + .parameters_updated = fira_session_fsm_active_parameters_updated, + .start = fira_session_fsm_active_start, + .stop = fira_session_fsm_active_stop, + .get_demand = fira_session_fsm_active_get_demand, + .get_access = fira_session_fsm_active_get_access, + .access_done = fira_session_fsm_active_access_done, + .check_missed_ranging = fira_session_fsm_active_check_missed_ranging, +}; diff --git a/mac/fira_session_fsm_active.h b/mac/fira_session_fsm_active.h new file mode 100644 index 0000000..f6ad54a --- /dev/null +++ b/mac/fira_session_fsm_active.h @@ -0,0 +1,31 @@ +/* + * This file is part of the UWB stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see <http://www.gnu.org/licenses/>. + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. Please contact Qorvo to inquire about licensing terms. + */ + +#ifndef NET_MCPS802154_FIRA_SESSION_FSM_ACTIVE_H +#define NET_MCPS802154_FIRA_SESSION_FSM_ACTIVE_H + +#include "fira_session_fsm.h" + +extern const struct fira_session_fsm_state fira_session_fsm_active; + +#endif /* NET_MCPS802154_FIRA_SESSION_FSM_ACTIVE_H */ diff --git a/mac/fira_session_fsm_idle.c b/mac/fira_session_fsm_idle.c new file mode 100644 index 0000000..d8235cf --- /dev/null +++ b/mac/fira_session_fsm_idle.c @@ -0,0 +1,136 @@ +/* + * This file is part of the UWB stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see <http://www.gnu.org/licenses/>. + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. Please contact Qorvo to inquire about licensing terms. + */ +#include <net/mcps802154_frame.h> + +#include "fira_session_fsm_init.h" +#include "fira_session_fsm_idle.h" +#include "fira_session_fsm_active.h" +#include "fira_session.h" +#include "fira_trace.h" + +static void +fira_session_fsm_idle_parameters_updated(struct fira_local *local, + struct fira_session *session) +{ + const struct fira_session_params *params = &session->params; + + if (session->measurements.reset) { + session->measurements.reset = false; + session->measurements.sequence = params->meas_seq; + } + if (!fira_session_is_ready(local, session)) { + fira_session_fsm_change_state(local, session, + &fira_session_fsm_init); + } +} + +static void +fira_session_fsm_idle_controlee_list_updated(struct fira_local *local, + struct fira_session *session) +{ + if (!fira_session_is_ready(local, session)) { + fira_session_fsm_change_state(local, session, + &fira_session_fsm_init); + } +} + +static int fira_session_fsm_idle_start(struct fira_local *local, + struct fira_session *session, + const struct genl_info *info) +{ + const struct fira_session_params *params = &session->params; + struct mcps802154_hrp_uwb_params *hrp = &session->hrp_uwb_params; + u32 now_dtu; + int r; + int i; + + trace_region_fira_session_params(session, params); + for (i = 0; i < params->meas_seq.n_steps; i++) { + const struct fira_measurement_sequence_step *step; + + step = ¶ms->meas_seq.steps[i]; + trace_region_fira_session_meas_seq_params(session, step, i); + } + + r = fira_crypto_derive_per_session(local, session); + if (r) + return r; + r = fira_crypto_derive_per_rotation(local, session, 0); + if (r) + return r; + r = mcps802154_get_current_timestamp_dtu(local->llhw, &now_dtu); + if (r) + return r; + + /* Update session. */ + session->event_portid = info->snd_portid; + session->block_start_valid = false; + session->block_index = 0; + session->sts_index = session->crypto.sts_index_init; + session->controlee.synchronised = false; + session->last_access_timestamp_dtu = now_dtu; + + /* Set radio parameters. */ + switch (params->prf_mode) { + case FIRA_PRF_MODE_BPRF: + hrp->prf = MCPS802154_PRF_64; + break; + case FIRA_PRF_MODE_HPRF: + hrp->prf = MCPS802154_PRF_125; + break; + default: + hrp->prf = MCPS802154_PRF_250; + break; + } + hrp->psr = params->preamble_duration == FIRA_PREAMBULE_DURATION_64 ? + MCPS802154_PSR_64 : + MCPS802154_PSR_32; + hrp->sfd_selector = (enum mcps802154_sfd)params->sfd_id; + hrp->phr_hi_rate = params->phr_data_rate == FIRA_PHR_DATA_RATE_6M81; + switch (params->psdu_data_rate) { + default: + case FIRA_PSDU_DATA_RATE_6M81: + hrp->data_rate = MCPS802154_DATA_RATE_6M81; + break; + case FIRA_PSDU_DATA_RATE_7M80: + hrp->data_rate = MCPS802154_DATA_RATE_7M80; + break; + case FIRA_PSDU_DATA_RATE_27M2: + hrp->data_rate = MCPS802154_DATA_RATE_27M2; + break; + case FIRA_PSDU_DATA_RATE_31M2: + hrp->data_rate = MCPS802154_DATA_RATE_31M2; + break; + } + fira_session_fsm_change_state(local, session, &fira_session_fsm_active); + + mcps802154_reschedule(local->llhw); + return 0; +} + +const struct fira_session_fsm_state fira_session_fsm_idle = { + .id = FIRA_SESSION_STATE_ID_IDLE, + .parameters_updated = fira_session_fsm_idle_parameters_updated, + .controlee_list_updated = fira_session_fsm_idle_controlee_list_updated, + .start = fira_session_fsm_idle_start, +}; diff --git a/mac/simple_ranging_region.h b/mac/fira_session_fsm_idle.h index 676863e..a8dd0aa 100644 --- a/mac/simple_ranging_region.h +++ b/mac/fira_session_fsm_idle.h @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. @@ -21,10 +21,11 @@ * Qorvo. Please contact Qorvo to inquire about licensing terms. */ -#ifndef NET_MCPS802154_SIMPLE_RANGING_REGION_H -#define NET_MCPS802154_SIMPLE_RANGING_REGION_H +#ifndef NET_MCPS802154_FIRA_SESSION_FSM_IDLE_H +#define NET_MCPS802154_FIRA_SESSION_FSM_IDLE_H -int simple_ranging_region_init(void); -void simple_ranging_region_exit(void); +#include "fira_session_fsm.h" -#endif /* NET_MCPS802154_SIMPLE_RANGING_REGION_H */ +extern const struct fira_session_fsm_state fira_session_fsm_idle; + +#endif /* NET_MCPS802154_FIRA_SESSION_FSM_IDLE_H */ diff --git a/mac/fira_session_fsm_init.c b/mac/fira_session_fsm_init.c new file mode 100644 index 0000000..c277721 --- /dev/null +++ b/mac/fira_session_fsm_init.c @@ -0,0 +1,73 @@ +/* + * This file is part of the UWB stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see <http://www.gnu.org/licenses/>. + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. Please contact Qorvo to inquire about licensing terms. + */ +#include <net/mcps802154_frame.h> + +#include "fira_session_fsm_init.h" +#include "fira_session_fsm_idle.h" +#include "fira_session_fsm_active.h" +#include "fira_session.h" + +static void fira_session_fsm_init_enter(struct fira_local *local, + struct fira_session *session) +{ + const struct fira_session_params *params = &session->params; + + session->measurements.sequence = params->meas_seq; + + if (fira_session_is_ready(local, session)) { + fira_session_fsm_change_state(local, session, + &fira_session_fsm_idle); + } +} + +void fira_session_fsm_init_parameters_updated(struct fira_local *local, + struct fira_session *session) +{ + const struct fira_session_params *params = &session->params; + + if (session->measurements.reset) { + session->measurements.reset = false; + session->measurements.sequence = params->meas_seq; + } + if (fira_session_is_ready(local, session)) { + fira_session_fsm_change_state(local, session, + &fira_session_fsm_idle); + } +} + +static void +fira_session_fsm_init_controlee_list_updated(struct fira_local *local, + struct fira_session *session) +{ + if (fira_session_is_ready(local, session)) { + fira_session_fsm_change_state(local, session, + &fira_session_fsm_idle); + } +} + +const struct fira_session_fsm_state fira_session_fsm_init = { + .id = FIRA_SESSION_STATE_ID_INIT, + .enter = fira_session_fsm_init_enter, + .parameters_updated = fira_session_fsm_init_parameters_updated, + .controlee_list_updated = fira_session_fsm_init_controlee_list_updated, +}; diff --git a/mac/fira_session_fsm_init.h b/mac/fira_session_fsm_init.h new file mode 100644 index 0000000..5191a2e --- /dev/null +++ b/mac/fira_session_fsm_init.h @@ -0,0 +1,31 @@ +/* + * This file is part of the UWB stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see <http://www.gnu.org/licenses/>. + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. Please contact Qorvo to inquire about licensing terms. + */ + +#ifndef NET_MCPS802154_FIRA_SESSION_FSM_INIT_H +#define NET_MCPS802154_FIRA_SESSION_FSM_INIT_H + +#include "fira_session_fsm.h" + +extern const struct fira_session_fsm_state fira_session_fsm_init; + +#endif /* NET_MCPS802154_FIRA_SESSION_FSM_INIT_H */ diff --git a/mac/fira_trace.h b/mac/fira_trace.h index 8e89247..cfca35f 100644 --- a/mac/fira_trace.h +++ b/mac/fira_trace.h @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2020-2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. @@ -30,6 +30,7 @@ #include <linux/tracepoint.h> #include "fira_session.h" #include "net/fira_region_params.h" +#include <net/fira_region_nl.h> /* clang-format off */ @@ -82,6 +83,18 @@ TRACE_DEFINE_ENUM(FIRA_RFRAME_CONFIG_SP3); TRACE_DEFINE_ENUM(FIRA_PREAMBULE_DURATION_32); TRACE_DEFINE_ENUM(FIRA_PREAMBULE_DURATION_64); +#define FIRA_STS_SEGMENTS_SYMBOLS \ + { FIRA_STS_SEGMENTS_0, "0" }, \ + { FIRA_STS_SEGMENTS_1, "1" }, \ + { FIRA_STS_SEGMENTS_2, "2" }, \ + { FIRA_STS_SEGMENTS_3, "3" }, \ + { FIRA_STS_SEGMENTS_4, "4" } +TRACE_DEFINE_ENUM(FIRA_STS_SEGMENTS_0); +TRACE_DEFINE_ENUM(FIRA_STS_SEGMENTS_1); +TRACE_DEFINE_ENUM(FIRA_STS_SEGMENTS_2); +TRACE_DEFINE_ENUM(FIRA_STS_SEGMENTS_3); +TRACE_DEFINE_ENUM(FIRA_STS_SEGMENTS_4); + #define FIRA_PSDU_DATA_RATE_SYMBOLS \ { FIRA_PSDU_DATA_RATE_6M81, "6M81" }, \ { FIRA_PSDU_DATA_RATE_7M80, "7M80" }, \ @@ -93,9 +106,9 @@ TRACE_DEFINE_ENUM(FIRA_PSDU_DATA_RATE_27M2); TRACE_DEFINE_ENUM(FIRA_PSDU_DATA_RATE_31M2); #define FIRA_PHR_DATA_RATE_SYMBOLS \ - { FIRA_PHR_DATA_RATE_850k, "850k" }, \ + { FIRA_PHR_DATA_RATE_850K, "850k" }, \ { FIRA_PHR_DATA_RATE_6M81, "6M81" } -TRACE_DEFINE_ENUM(FIRA_PHR_DATA_RATE_850k); +TRACE_DEFINE_ENUM(FIRA_PHR_DATA_RATE_850K); TRACE_DEFINE_ENUM(FIRA_PHR_DATA_RATE_6M81); #define FIRA_MAC_FCS_TYPE_CRC_SYMBOLS \ @@ -128,25 +141,39 @@ TRACE_DEFINE_ENUM(FIRA_MESSAGE_ID_MEASUREMENT_REPORT); TRACE_DEFINE_ENUM(FIRA_MESSAGE_ID_RESULT_REPORT); TRACE_DEFINE_ENUM(FIRA_MESSAGE_ID_CONTROL_UPDATE); -#define FIRA_RANGING_STATUS \ - { FIRA_STATUS_RANGING_SUCCESS, "success" }, \ - { FIRA_STATUS_RANGING_TX_FAILED, "tx_failed" }, \ - { FIRA_STATUS_RANGING_RX_TIMEOUT, "rx_timeout" }, \ - { FIRA_STATUS_RANGING_RX_PHY_DEC_FAILED, "rx_phy_dec_failed" }, \ - { FIRA_STATUS_RANGING_RX_PHY_TOA_FAILED, "rx_phy_toa_failed" }, \ - { FIRA_STATUS_RANGING_RX_PHY_STS_FAILED, "rx_phy_sts_failed" }, \ - { FIRA_STATUS_RANGING_RX_MAC_DEC_FAILED, "rx_mac_dec_failed" }, \ - { FIRA_STATUS_RANGING_RX_MAC_IE_DEC_FAILED, "rx_mac_ie_dec_failed" }, \ - { FIRA_STATUS_RANGING_RX_MAC_IE_MISSING, "rx_mac_ie_missing" } -TRACE_DEFINE_ENUM(FIRA_STATUS_RANGING_SUCCESS); -TRACE_DEFINE_ENUM(FIRA_STATUS_RANGING_TX_FAILED); -TRACE_DEFINE_ENUM(FIRA_STATUS_RANGING_RX_TIMEOUT); -TRACE_DEFINE_ENUM(FIRA_STATUS_RANGING_RX_PHY_DEC_FAILED); -TRACE_DEFINE_ENUM(FIRA_STATUS_RANGING_RX_PHY_TOA_FAILED); -TRACE_DEFINE_ENUM(FIRA_STATUS_RANGING_RX_PHY_STS_FAILED); -TRACE_DEFINE_ENUM(FIRA_STATUS_RANGING_RX_MAC_DEC_FAILED); -TRACE_DEFINE_ENUM(FIRA_STATUS_RANGING_RX_MAC_IE_DEC_FAILED); -TRACE_DEFINE_ENUM(FIRA_STATUS_RANGING_RX_MAC_IE_MISSING); +#define mcps802154_rx_error_name(name) \ + { \ + MCPS802154_RX_ERROR_##name, #name \ + } +#define MCPS802154_RX_ERROR_SYMBOLS \ + mcps802154_rx_error_name(NONE), \ + mcps802154_rx_error_name(TIMEOUT), \ + mcps802154_rx_error_name(BAD_CKSUM), \ + mcps802154_rx_error_name(UNCORRECTABLE), \ + mcps802154_rx_error_name(FILTERED), \ + mcps802154_rx_error_name(SFD_TIMEOUT), \ + mcps802154_rx_error_name(PHR_DECODE), \ + mcps802154_rx_error_name(OTHER) +TRACE_DEFINE_ENUM(MCPS802154_RX_ERROR_NONE); +TRACE_DEFINE_ENUM(MCPS802154_RX_ERROR_TIMEOUT); +TRACE_DEFINE_ENUM(MCPS802154_RX_ERROR_BAD_CKSUM); +TRACE_DEFINE_ENUM(MCPS802154_RX_ERROR_UNCORRECTABLE); +TRACE_DEFINE_ENUM(MCPS802154_RX_ERROR_FILTERED); +TRACE_DEFINE_ENUM(MCPS802154_RX_ERROR_SFD_TIMEOUT); +TRACE_DEFINE_ENUM(MCPS802154_RX_ERROR_PHR_DECODE); +TRACE_DEFINE_ENUM(MCPS802154_RX_ERROR_OTHER); + +#define mcps802154_tx_reason_name(name) \ + { \ + MCPS802154_ACCESS_TX_RETURN_REASON_##name, #name \ + } +#define MCPS802154_TX_REASON_SYMBOLS \ + mcps802154_tx_reason_name(CONSUMED), \ + mcps802154_tx_reason_name(FAILURE), \ + mcps802154_tx_reason_name(CANCEL) +TRACE_DEFINE_ENUM(MCPS802154_ACCESS_TX_RETURN_REASON_CONSUMED); +TRACE_DEFINE_ENUM(MCPS802154_ACCESS_TX_RETURN_REASON_FAILURE); +TRACE_DEFINE_ENUM(MCPS802154_ACCESS_TX_RETURN_REASON_CANCEL); #define FIRA_MEAS_SEQ_STEP_TYPE \ { FIRA_MEASUREMENT_TYPE_RANGE, "range" }, \ @@ -161,6 +188,56 @@ TRACE_DEFINE_ENUM(FIRA_MEASUREMENT_TYPE_AOA_AZIMUTH); TRACE_DEFINE_ENUM(FIRA_MEASUREMENT_TYPE_AOA_ELEVATION); TRACE_DEFINE_ENUM(FIRA_MEASUREMENT_TYPE_AOA_AZIMUTH_ELEVATION); +#define fira_session_state_id_name(name) \ + { \ + FIRA_SESSION_STATE_ID_##name, #name \ + } +#define FIRA_SESSION_STATE_ID_SYMBOLS \ + fira_session_state_id_name(DEINIT), \ + fira_session_state_id_name(INIT), \ + fira_session_state_id_name(ACTIVE), \ + fira_session_state_id_name(IDLE) +TRACE_DEFINE_ENUM(FIRA_SESSION_STATE_ID_DEINIT); +TRACE_DEFINE_ENUM(FIRA_SESSION_STATE_ID_INIT); +TRACE_DEFINE_ENUM(FIRA_SESSION_STATE_ID_ACTIVE); +TRACE_DEFINE_ENUM(FIRA_SESSION_STATE_ID_IDLE); + + +#define fira_call_name(name) \ + { \ + FIRA_CALL_##name, #name \ + } +#define FIRA_CALL_SYMBOLS \ + fira_call_name(GET_CAPABILITIES), \ + fira_call_name(SESSION_INIT), \ + fira_call_name(SESSION_START), \ + fira_call_name(SESSION_STOP), \ + fira_call_name(SESSION_DEINIT), \ + fira_call_name(SESSION_SET_PARAMS), \ + fira_call_name(NEW_CONTROLEE), \ + fira_call_name(DEL_CONTROLEE), \ + fira_call_name(SESSION_NOTIFICATION), \ + fira_call_name(SESSION_GET_PARAMS), \ + fira_call_name(SESSION_GET_STATE), \ + fira_call_name(SESSION_GET_COUNT), \ + fira_call_name(SET_CONTROLEE), \ + fira_call_name(GET_CONTROLEES) +TRACE_DEFINE_ENUM(FIRA_CALL_GET_CAPABILITIES); +TRACE_DEFINE_ENUM(FIRA_CALL_SESSION_INIT); +TRACE_DEFINE_ENUM(FIRA_CALL_SESSION_START); +TRACE_DEFINE_ENUM(FIRA_CALL_SESSION_STOP); +TRACE_DEFINE_ENUM(FIRA_CALL_SESSION_DEINIT); +TRACE_DEFINE_ENUM(FIRA_CALL_SESSION_SET_PARAMS); +TRACE_DEFINE_ENUM(FIRA_CALL_NEW_CONTROLEE); +TRACE_DEFINE_ENUM(FIRA_CALL_DEL_CONTROLEE); +TRACE_DEFINE_ENUM(FIRA_CALL_SESSION_NOTIFICATION); +TRACE_DEFINE_ENUM(FIRA_CALL_SESSION_GET_PARAMS); +TRACE_DEFINE_ENUM(FIRA_CALL_SESSION_GET_STATE); +TRACE_DEFINE_ENUM(FIRA_CALL_SESSION_GET_COUNT); +TRACE_DEFINE_ENUM(FIRA_CALL_SET_CONTROLEE); +TRACE_DEFINE_ENUM(FIRA_CALL_GET_CONTROLEES); + + TRACE_EVENT(region_fira_session_params, TP_PROTO(const struct fira_session *session, const struct fira_session_params *params), @@ -185,6 +262,7 @@ TRACE_EVENT(region_fira_session_params, __field(enum fira_rframe_config, rframe_config) __field(enum fira_preambule_duration, preamble_duration) __field(enum fira_sfd_id, sfd_id) + __field(enum fira_sts_segments, number_of_sts_segments) __field(enum fira_psdu_data_rate, psdu_data_rate) __field(enum fira_mac_fcs_type, mac_fcs_type) __field(enum fira_sts_config, sts_config) @@ -194,6 +272,7 @@ TRACE_EVENT(region_fira_session_params, __field(bool, report_aoa_azimuth) __field(bool, report_aoa_elevation) __field(bool, report_aoa_fom) + __field(bool, report_diagnostics) ), TP_fast_assign( FIRA_SESSION_ASSIGN; @@ -215,6 +294,7 @@ TRACE_EVENT(region_fira_session_params, __entry->rframe_config = params->rframe_config; __entry->preamble_duration = params->preamble_duration; __entry->sfd_id = params->sfd_id; + __entry->number_of_sts_segments = params->number_of_sts_segments; __entry->psdu_data_rate = params->psdu_data_rate; __entry->mac_fcs_type = params->mac_fcs_type; __entry->sts_config = params->sts_config; @@ -224,15 +304,16 @@ TRACE_EVENT(region_fira_session_params, __entry->report_aoa_azimuth = params->report_aoa_azimuth; __entry->report_aoa_elevation = params->report_aoa_elevation; __entry->report_aoa_fom = params->report_aoa_fom; + __entry->report_diagnostics = params->report_diagnostics; ), TP_printk(FIRA_SESSION_PR_FMT " device_type=%s ranging_round_usage=%s multi_node_mode=%s " "controller_short_addr=0x%x initiation_time_ms=%d slot_duration_dtu=%d " "block_duration_dtu=%d block_stride_len=%d max_nb_of_measurements=%d " "max_rr_retry=%d round_duration_slots=%d round_hopping=%d " "priority=%d channel_number=%d preamble_code_index=%d rframe_config=%s " - "preamble_duration=%s sfd_id=%d psdu_data_rate=%s mac_fcs_type=%s " + "preamble_duration=%s sfd_id=%d number_of_sts_segments=%s psdu_data_rate=%s mac_fcs_type=%s " "sts_config=%s vupper64=%s aoa_result_req=%d report_tof=%d report_aoa_azimuth=%d " - "report_aoa_elevation=%d report_aoa_fom=%d", + "report_aoa_elevation=%d report_aoa_fom=%d diagnostics=%d", FIRA_SESSION_PR_ARG, __print_symbolic(__entry->device_type, FIRA_DEVICE_TYPE_SYMBOLS), __print_symbolic(__entry->ranging_round_usage, FIRA_RANGING_ROUND_SYMBOLS), @@ -252,6 +333,7 @@ TRACE_EVENT(region_fira_session_params, __print_symbolic(__entry->rframe_config, FIRA_RFRAME_CONFIG_SYMBOLS), __print_symbolic(__entry->preamble_duration, FIRA_PREAMBULE_DURATION_SYMBOLS), __entry->sfd_id, + __print_symbolic(__entry->number_of_sts_segments, FIRA_STS_SEGMENTS_SYMBOLS), __print_symbolic(__entry->psdu_data_rate, FIRA_PSDU_DATA_RATE_SYMBOLS), __print_symbolic(__entry->mac_fcs_type, FIRA_MAC_FCS_TYPE_CRC_SYMBOLS), __print_symbolic(__entry->sts_config, FIRA_STS_CONFIG_SYMBOLS), @@ -260,33 +342,233 @@ TRACE_EVENT(region_fira_session_params, __entry->report_tof, __entry->report_aoa_azimuth, __entry->report_aoa_elevation, - __entry->report_aoa_fom + __entry->report_aoa_fom, + __entry->report_diagnostics + ) + ); + +TRACE_EVENT(region_fira_session_meas_seq_params, + TP_PROTO(const struct fira_session *session, + const struct fira_measurement_sequence_step *step, + int index), + TP_ARGS(session, step, index), + TP_STRUCT__entry( + FIRA_SESSION_ENTRY + __field(int, index) + __field(enum fira_measurement_type, type) + __field(u8, n_measurements) + __field(s8, rx_ant_set_nonranging) + __field(s8, rx_ant_sets_ranging_0) + __field(s8, rx_ant_sets_ranging_1) + __field(s8, tx_ant_set_nonranging) + __field(s8, tx_ant_set_ranging) + ), + TP_fast_assign( + FIRA_SESSION_ASSIGN; + __entry->index = index; + __entry->type = step->type; + __entry->n_measurements = step->n_measurements; + __entry->rx_ant_set_nonranging = step->rx_ant_set_nonranging; + __entry->rx_ant_sets_ranging_0 = step->rx_ant_sets_ranging[0]; + __entry->rx_ant_sets_ranging_1 = step->rx_ant_sets_ranging[1]; + __entry->tx_ant_set_nonranging = step->tx_ant_set_nonranging; + __entry->tx_ant_set_ranging = step->tx_ant_set_ranging; + ), + TP_printk(FIRA_SESSION_PR_FMT " index=%d type=%s n_measurements=%d " + "rx_ant_set_nonranging=%d rx_ant_sets_ranging_0=%d " + "rx_ant_sets_ranging_1=%d tx_ant_set_nonranging=%d " + "tx_ant_set_ranging=%d", + FIRA_SESSION_PR_ARG, + __entry->index, + __print_symbolic(__entry->type, FIRA_MEAS_SEQ_STEP_TYPE), + __entry->n_measurements, + __entry->rx_ant_set_nonranging, + __entry->rx_ant_sets_ranging_0, + __entry->rx_ant_sets_ranging_1, + __entry->tx_ant_set_nonranging, + __entry->tx_ant_set_ranging) + ); + +TRACE_EVENT(region_fira_session_control, + TP_PROTO(const struct fira_local *local, + int session_id, enum fira_call call_id), + TP_ARGS(local, session_id, call_id), + TP_STRUCT__entry( + FIRA_SESSION_ENTRY + __field(enum fira_call, call_id) + ), + TP_fast_assign( + __entry->session_id = session_id; + __entry->call_id = call_id; + ), + TP_printk(FIRA_SESSION_PR_FMT " call_id=%s", + FIRA_SESSION_PR_ARG, + __print_symbolic(__entry->call_id, FIRA_CALL_SYMBOLS) + ) + ); + +TRACE_EVENT( + region_fira_session_fsm_active_get_demand_return, + TP_PROTO(const struct fira_local *local, + const struct fira_session *session, + const struct fira_session_demand *fsd), + TP_ARGS(local, session, fsd), + TP_STRUCT__entry( + FIRA_SESSION_ENTRY + __field(u32, block_start_dtu) + __field(u32, timestamp_dtu) + __field(int, max_duration_dtu) + ), + TP_fast_assign( + FIRA_SESSION_ASSIGN; + __entry->block_start_dtu = fsd->block_start_dtu; + __entry->timestamp_dtu = fsd->timestamp_dtu; + __entry->max_duration_dtu = fsd->max_duration_dtu; + ), + TP_printk(FIRA_SESSION_PR_FMT " block_start_dtu=%#x " + "timestamp_dtu=%#x max_duration_dtu=%d", + FIRA_SESSION_PR_ARG, + __entry->block_start_dtu, + __entry->timestamp_dtu, + __entry->max_duration_dtu + ) + ); + +TRACE_EVENT( + region_fira_get_access_controller, + TP_PROTO(const struct fira_local *local, + const struct fira_session *session, + const struct fira_session_demand *fsd), + TP_ARGS(local, session, fsd), + TP_STRUCT__entry( + FIRA_SESSION_ENTRY + __field(int, block_index) + __field(int, add_blocks) + __field(int, round_index) + __field(int, block_stride_len) + __field(u32, block_start_dtu) + __field(u32, timestamp_dtu) + __field(int, max_duration_dtu) + ), + TP_fast_assign( + FIRA_SESSION_ASSIGN; + __entry->block_index = session->block_index; + __entry->add_blocks = fsd->add_blocks; + __entry->round_index = fsd->round_index; + __entry->block_stride_len = session->block_stride_len; + __entry->block_start_dtu = fsd->block_start_dtu; + __entry->timestamp_dtu = fsd->timestamp_dtu; + __entry->max_duration_dtu = fsd->max_duration_dtu; + ), + TP_printk(FIRA_SESSION_PR_FMT " block_index=%d add_blocks=%d " + "round_index=%d block_stride_len=%d block_start_dtu=%#x " + "timestamp_dtu=%#x max_duration_dtu=%d", + FIRA_SESSION_PR_ARG, + __entry->block_index, + __entry->add_blocks, + __entry->round_index, + __entry->block_stride_len, + __entry->block_start_dtu, + __entry->timestamp_dtu, + __entry->max_duration_dtu ) ); -TRACE_EVENT(region_fira_rx_message, +TRACE_EVENT( + region_fira_get_access_controlee, + TP_PROTO(const struct fira_local *local, + const struct fira_session *session, + const struct fira_session_demand *fsd), + TP_ARGS(local, session, fsd), + TP_STRUCT__entry( + FIRA_SESSION_ENTRY + __field(int, block_index) + __field(int, add_blocks) + __field(int, round_index) + __field(u32, block_start_dtu) + __field(u32, timestamp_dtu) + __field(int, max_duration_dtu) + ), + TP_fast_assign( + FIRA_SESSION_ASSIGN; + __entry->block_index = session->block_index; + __entry->add_blocks = fsd->add_blocks; + __entry->round_index = fsd->round_index; + __entry->block_start_dtu = fsd->block_start_dtu; + __entry->timestamp_dtu = fsd->timestamp_dtu; + __entry->max_duration_dtu = fsd->max_duration_dtu; + ), + TP_printk(FIRA_SESSION_PR_FMT " block_index=%d add_blocks=%d " + "round_index=%d block_start_dtu=%#x timestamp_dtu=%#x " + "max_duration_dtu=%d", + FIRA_SESSION_PR_ARG, + __entry->block_index, + __entry->add_blocks, + __entry->round_index, + __entry->block_start_dtu, + __entry->timestamp_dtu, + __entry->max_duration_dtu + ) + ); + +TRACE_EVENT(region_fira_rx_frame, TP_PROTO(const struct fira_session *session, enum fira_message_id message_id, - enum fira_ranging_status status), - TP_ARGS(session, message_id, status), + enum mcps802154_rx_error_type error), + TP_ARGS(session, message_id, error), TP_STRUCT__entry( FIRA_SESSION_ENTRY __field(enum fira_message_id, message_id) - __field(enum fira_ranging_status, status) + __field(enum mcps802154_rx_error_type, error) ), TP_fast_assign( FIRA_SESSION_ASSIGN; __entry->message_id = message_id; - __entry->status = status; + __entry->error = error; ), - TP_printk(FIRA_SESSION_PR_FMT " message_id=%s status=%s", + TP_printk(FIRA_SESSION_PR_FMT " message_id=%s error=%s", FIRA_SESSION_PR_ARG, __print_symbolic(__entry->message_id, FIRA_MESSAGE_TYPE), - __print_symbolic(__entry->status, FIRA_RANGING_STATUS) + __print_symbolic(__entry->error, MCPS802154_RX_ERROR_SYMBOLS) + ) + ); + +TRACE_EVENT(region_fira_rx_frame_control, + TP_PROTO(const struct fira_local *local, + const struct fira_session *session, + int left_duration_dtu, int n_slots), + TP_ARGS(local, session, left_duration_dtu, n_slots), + TP_STRUCT__entry( + FIRA_SESSION_ENTRY + __field(u32, block_start_dtu) + __field(int, block_index) + __field(int, round_index) + __field(bool, stop_inband) + __field(int, left_duration_dtu) + __field(int, n_slots) + ), + TP_fast_assign( + FIRA_SESSION_ASSIGN; + __entry->block_start_dtu = session->block_start_dtu; + __entry->block_index = session->block_index; + __entry->round_index = session->round_index; + __entry->stop_inband = session->stop_inband; + __entry->left_duration_dtu = left_duration_dtu; + __entry->n_slots = n_slots; + ), + TP_printk(FIRA_SESSION_PR_FMT " block_start_dtu=%#x block_index=%d " + "round_index=%d stop_inband=%s left_duration_dtu=%d n_slots=%d", + FIRA_SESSION_PR_ARG, + __entry->block_start_dtu, + __entry->block_index, + __entry->round_index, + __entry->stop_inband ? "true": "false", + __entry->left_duration_dtu, + __entry->n_slots ) ); -TRACE_EVENT(region_fira_tx_message, +TRACE_EVENT(region_fira_tx_get_frame, TP_PROTO(const struct fira_session *session, enum fira_message_id message_id), TP_ARGS(session, message_id), @@ -304,56 +586,140 @@ TRACE_EVENT(region_fira_tx_message, ) ); -TRACE_EVENT(fira_nondeferred_not_supported, - TP_PROTO(const struct fira_session *session), - TP_ARGS(session), - TP_STRUCT__entry(FIRA_SESSION_ENTRY), - TP_fast_assign(FIRA_SESSION_ASSIGN;), - TP_printk(FIRA_SESSION_PR_FMT "FiRa non-deferred mode ranging not supported yet", - FIRA_SESSION_PR_ARG +TRACE_EVENT(region_fira_tx_return, + TP_PROTO(const struct fira_session *session, + enum mcps802154_access_tx_return_reason reason), + TP_ARGS(session, reason), + TP_STRUCT__entry( + FIRA_SESSION_ENTRY + __field(enum mcps802154_access_tx_return_reason, reason) + ), + TP_fast_assign( + FIRA_SESSION_ASSIGN; + __entry->reason = reason; + ), + TP_printk(FIRA_SESSION_PR_FMT " reason=%s", + FIRA_SESSION_PR_ARG, + __print_symbolic(__entry->reason, + MCPS802154_TX_REASON_SYMBOLS) + ) + ); + +TRACE_EVENT( + region_fira_session_fsm_change_state, + TP_PROTO(const struct fira_session *session, enum fira_session_state_id new_state_id), + TP_ARGS(session, new_state_id), + TP_STRUCT__entry( + FIRA_SESSION_ENTRY + __field(enum fira_session_state_id, new_state_id) + ), + TP_fast_assign( + FIRA_SESSION_ASSIGN; + __entry->new_state_id = new_state_id; + ), + TP_printk(FIRA_SESSION_PR_FMT " new_state_id=%s", + FIRA_SESSION_PR_ARG, + __print_symbolic(__entry->new_state_id, + FIRA_SESSION_STATE_ID_SYMBOLS) ) ); -TRACE_EVENT(region_fira_meas_seq_step, +TRACE_EVENT( + region_fira_access_done, + TP_PROTO(const struct fira_local *local, + const struct fira_session *session, + int access_duration_dtu, bool error), + TP_ARGS(local, session, access_duration_dtu, error), + TP_STRUCT__entry( + FIRA_SESSION_ENTRY + __field(int, access_duration_dtu) + __field(bool, error) + ), + TP_fast_assign( + FIRA_SESSION_ASSIGN; + __entry->access_duration_dtu = access_duration_dtu; + __entry->error = error; + ), + TP_printk(FIRA_SESSION_PR_FMT " access_duration_dtu=%d error=%s", + FIRA_SESSION_PR_ARG, + __entry->access_duration_dtu, + __entry->error ? "true": "false" + ) + ); + +TRACE_EVENT( + region_fira_is_controlee_synchronised, TP_PROTO(const struct fira_session *session, - const struct fira_measurement_sequence_step *step, - u8 current_step), - TP_ARGS(session, step, current_step), + int drift_ppm, int rx_margin_ppm), + TP_ARGS(session, drift_ppm, rx_margin_ppm), TP_STRUCT__entry( FIRA_SESSION_ENTRY - __field(u8, step_nb) - __field(enum fira_measurement_type, type) - __field(u8, n_measurements) - __field(s8, rx_ant_set_nonranging) - __field(s8, rx_ant_sets_ranging_0) - __field(s8, rx_ant_sets_ranging_1) - __field(s8, tx_ant_set_nonranging) - __field(s8, tx_ant_set_ranging) - ), + __field(int, block_index_sync) + __field(int, drift_ppm) + __field(int, rx_margin_ppm) + ), TP_fast_assign( FIRA_SESSION_ASSIGN; - __entry->step_nb = current_step; - __entry->type = step->type; - __entry->n_measurements = step->n_measurements; - __entry->rx_ant_set_nonranging = step->rx_ant_set_nonranging; - __entry->rx_ant_sets_ranging_0 = step->rx_ant_sets_ranging[0]; - __entry->rx_ant_sets_ranging_1 = step->rx_ant_sets_ranging[1]; - __entry->tx_ant_set_nonranging = step->tx_ant_set_nonranging; - __entry->tx_ant_set_ranging = step->tx_ant_set_ranging; + __entry->block_index_sync = session->controlee.block_index_sync; + __entry->drift_ppm = drift_ppm; + __entry->rx_margin_ppm = rx_margin_ppm; ), - TP_printk(FIRA_SESSION_PR_FMT " step #%d : type=%s, n_measurements=%d, " - "ant_sets : RxNR=%d, RxR[0]=%d, RxR[1]=%d, TxNR=%d, TxR=%d", + TP_printk(FIRA_SESSION_PR_FMT " block_index_sync=%d drift_ppm=%d rx_margin_ppm=%d", FIRA_SESSION_PR_ARG, - __entry->step_nb, - __print_symbolic(__entry->type, FIRA_MEAS_SEQ_STEP_TYPE), - __entry->n_measurements, - __entry->rx_ant_set_nonranging, - __entry->rx_ant_sets_ranging_0, - __entry->rx_ant_sets_ranging_1, - __entry->tx_ant_set_nonranging, - __entry->tx_ant_set_ranging) + __entry->block_index_sync, + __entry->drift_ppm, + __entry->rx_margin_ppm + ) + ); + +TRACE_EVENT( + region_fira_session_report, + TP_PROTO(const struct fira_session *session, + const struct fira_report_info *report_info), + TP_ARGS(session, report_info), + TP_STRUCT__entry( + FIRA_SESSION_ENTRY + __field(int, sequence_number) + __field(int, block_index) + __field(int, n_ranging_data) + __field(int, n_stopped_controlees) + __field(int, n_slots) + __field(bool, stopped) + ), + TP_fast_assign( + FIRA_SESSION_ASSIGN; + __entry->sequence_number = session->sequence_number; + __entry->block_index = session->block_index; + __entry->n_ranging_data = report_info->n_ranging_data; + __entry->n_stopped_controlees = report_info->n_stopped_controlees; + __entry->n_slots = report_info->n_slots; + __entry->stopped = report_info->stopped; + ), + TP_printk(FIRA_SESSION_PR_FMT " sequence_number=%d block_index=%d " + "n_ranging_data=%d n_stopped_controlees=%d n_slots=%d stopped=%s", + FIRA_SESSION_PR_ARG, + __entry->sequence_number, + __entry->block_index, + __entry->n_ranging_data, + __entry->n_stopped_controlees, + __entry->n_slots, + __entry->stopped ? "true": "false" + ) ); +TRACE_EVENT(fira_nondeferred_not_supported, + TP_PROTO(const struct fira_session *session), + TP_ARGS(session), + TP_STRUCT__entry( + FIRA_SESSION_ENTRY + ), + TP_fast_assign( + FIRA_SESSION_ASSIGN; + ), + TP_printk(FIRA_SESSION_PR_FMT, + FIRA_SESSION_PR_ARG + ) + ); #endif /* !FIRA_TRACE_H || TRACE_HEADER_MULTI_READ */ diff --git a/mac/fproc.c b/mac/fproc.c index 0026f97..68ec4c0 100644 --- a/mac/fproc.c +++ b/mac/fproc.c @@ -37,6 +37,7 @@ void mcps802154_fproc_uninit(struct mcps802154_local *local) WARN_ON(local->fproc.access); WARN_ON(local->fproc.tx_skb); WARN_ON(local->started); + WARN_ON(local->fproc.deferred); } void mcps802154_fproc_change_state( @@ -74,6 +75,9 @@ void mcps802154_fproc_access(struct mcps802154_local *local, case MCPS802154_ACCESS_METHOD_NOTHING: r = mcps802154_fproc_nothing_handle(local, access); break; + case MCPS802154_ACCESS_METHOD_IDLE: + r = mcps802154_fproc_idle_handle(local, access); + break; case MCPS802154_ACCESS_METHOD_IMMEDIATE_RX: r = mcps802154_fproc_rx_handle(local, access); break; @@ -150,6 +154,22 @@ static void mcps802154_broken_safe(struct mcps802154_local *local) mcps802154_fproc_broken_handle(local); } +static void mcps802154_fproc_call_deferred(struct mcps802154_local *local) +{ + struct mcps802154_region *region = local->fproc.deferred; + + if (region) { + local->fproc.deferred = NULL; + region->ops->deferred(region); + } +} + +void mcps802154_fproc_schedule_change(struct mcps802154_local *local) +{ + local->fproc.state->schedule_change(local); + mcps802154_fproc_call_deferred(local); +} + void mcps802154_rx_frame(struct mcps802154_llhw *llhw) { struct mcps802154_local *local = llhw_to_local(llhw); @@ -160,6 +180,7 @@ void mcps802154_rx_frame(struct mcps802154_llhw *llhw) local->fproc.state->rx_frame(local); else mcps802154_broken_safe(local); + mcps802154_fproc_call_deferred(local); trace_llhw_event_done(local); mutex_unlock(&local->fsm_lock); } @@ -175,6 +196,7 @@ void mcps802154_rx_timeout(struct mcps802154_llhw *llhw) local->fproc.state->rx_timeout(local); else mcps802154_broken_safe(local); + mcps802154_fproc_call_deferred(local); trace_llhw_event_done(local); mutex_unlock(&local->fsm_lock); } @@ -191,6 +213,7 @@ void mcps802154_rx_error(struct mcps802154_llhw *llhw, local->fproc.state->rx_error(local, error); else mcps802154_broken_safe(local); + mcps802154_fproc_call_deferred(local); trace_llhw_event_done(local); mutex_unlock(&local->fsm_lock); } @@ -206,6 +229,7 @@ void mcps802154_tx_done(struct mcps802154_llhw *llhw) local->fproc.state->tx_done(local); else mcps802154_broken_safe(local); + mcps802154_fproc_call_deferred(local); trace_llhw_event_done(local); mutex_unlock(&local->fsm_lock); } @@ -223,7 +247,7 @@ void mcps802154_tx_too_late(struct mcps802154_llhw *llhw) trace_llhw_event_done(local); mutex_unlock(&local->fsm_lock); } -EXPORT_SYMBOL_GPL(mcps802154_tx_too_late); +EXPORT_SYMBOL(mcps802154_tx_too_late); void mcps802154_rx_too_late(struct mcps802154_llhw *llhw) { @@ -237,7 +261,7 @@ void mcps802154_rx_too_late(struct mcps802154_llhw *llhw) trace_llhw_event_done(local); mutex_unlock(&local->fsm_lock); } -EXPORT_SYMBOL_GPL(mcps802154_rx_too_late); +EXPORT_SYMBOL(mcps802154_rx_too_late); void mcps802154_broken(struct mcps802154_llhw *llhw) { @@ -246,6 +270,7 @@ void mcps802154_broken(struct mcps802154_llhw *llhw) mutex_lock(&local->fsm_lock); trace_llhw_event_broken(local); mcps802154_broken_safe(local); + mcps802154_fproc_call_deferred(local); trace_llhw_event_done(local); mutex_unlock(&local->fsm_lock); } @@ -259,6 +284,7 @@ void mcps802154_timer_expired(struct mcps802154_llhw *llhw) trace_llhw_event_timer_expired(local); if (local->fproc.state->timer_expired) local->fproc.state->timer_expired(local); + mcps802154_fproc_call_deferred(local); trace_llhw_event_done(local); mutex_unlock(&local->fsm_lock); } diff --git a/mac/fproc.h b/mac/fproc.h index c65e1a0..f26f55d 100644 --- a/mac/fproc.h +++ b/mac/fproc.h @@ -68,6 +68,8 @@ struct mcps802154_fproc { struct sk_buff *tx_skb; /** @frame_idx: Frame index for multiple frames method. */ size_t frame_idx; + /** @deferred: Pointer to region context requesting deferred call. */ + struct mcps802154_region *deferred; }; extern const struct mcps802154_fproc_state mcps802154_fproc_stopped; @@ -126,6 +128,15 @@ void mcps802154_fproc_access_done(struct mcps802154_local *local, bool error); void mcps802154_fproc_access_reset(struct mcps802154_local *local); /** + * mcps802154_fproc_schedule_change() - Try a schedule change. + * @local: MCPS private data. + * + * Inform the current state that the schedule has changed. To be called + * exclusively from CA. + */ +void mcps802154_fproc_schedule_change(struct mcps802154_local *local); + +/** * mcps802154_fproc_stopped_handle() - Go to stopped. * @local: MCPS private data. */ @@ -148,6 +159,17 @@ int mcps802154_fproc_nothing_handle(struct mcps802154_local *local, struct mcps802154_access *access); /** + * mcps802154_fproc_idle_handle() - Handle inactivity with trust in + * access->duration. + * @local: MCPS private data. + * @access: Current access to handle. + * + * Return: 0 or error. + */ +int mcps802154_fproc_idle_handle(struct mcps802154_local *local, + struct mcps802154_access *access); + +/** * mcps802154_fproc_rx_handle() - Handle an RX access and change state. * @local: MCPS private data. * @access: Current access to handle. diff --git a/mac/fproc_broken.c b/mac/fproc_broken.c index 36692f2..86bed74 100644 --- a/mac/fproc_broken.c +++ b/mac/fproc_broken.c @@ -23,9 +23,11 @@ #include <linux/printk.h> #include "mcps802154_i.h" +#include "trace.h" static void mcps802154_fproc_broken_enter(struct mcps802154_local *local) { + trace_fproc_broken_enter(local); pr_err_ratelimited("mcps802154: entering broken state for %s\n", wpan_phy_name(local->hw->phy)); local->broken = true; diff --git a/mac/fproc_idle.c b/mac/fproc_idle.c new file mode 100644 index 0000000..300ddb0 --- /dev/null +++ b/mac/fproc_idle.c @@ -0,0 +1,68 @@ +/* + * This file is part of the UWB stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see <http://www.gnu.org/licenses/>. + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. Please contact Qorvo to inquire about licensing terms. + */ + +#include "mcps802154_i.h" +#include "llhw-ops.h" + +static void mcps802154_fproc_idle_timer_expired(struct mcps802154_local *local) +{ + struct mcps802154_access *access = local->fproc.access; + + mcps802154_fproc_access_done(local, false); + if (access->duration_dtu) { + u32 next_access_dtu = + access->timestamp_dtu + access->duration_dtu; + + mcps802154_fproc_access(local, next_access_dtu); + } else { + mcps802154_fproc_access_now(local); + } +} + +static void +mcps802154_fproc_idle_schedule_change(struct mcps802154_local *local) +{ + mcps802154_fproc_access_done(local, false); + mcps802154_fproc_access_now(local); +} + +static const struct mcps802154_fproc_state mcps802154_fproc_idle = { + .name = "idle", + .timer_expired = mcps802154_fproc_idle_timer_expired, + .schedule_change = mcps802154_fproc_idle_schedule_change, +}; + +int mcps802154_fproc_idle_handle(struct mcps802154_local *local, + struct mcps802154_access *access) +{ + int r; + + r = llhw_idle(local, access->duration_dtu != 0, + access->timestamp_dtu + access->duration_dtu); + if (r) + return r; + + mcps802154_fproc_change_state(local, &mcps802154_fproc_idle); + + return 0; +} diff --git a/mac/fproc_multi.c b/mac/fproc_multi.c index 9ca05ad..ea1f8a6 100644 --- a/mac/fproc_multi.c +++ b/mac/fproc_multi.c @@ -89,7 +89,7 @@ mcps802154_fproc_multi_check_frames(struct mcps802154_local *local, const struct mcps802154_access_frame *frame = &access->frames[frame_idx]; /* Only first Rx can be without timeout. */ - if (!frame->is_tx && frame->rx.info.timeout_dtu == -1) + if (!frame->is_tx && frame->rx.frame_config.timeout_dtu == -1) return -EINVAL; } return 0; @@ -195,7 +195,7 @@ mcps802154_fproc_multi_rx_rx_error(struct mcps802154_local *local, struct mcps802154_access *access = local->fproc.access; size_t frame_idx = local->fproc.frame_idx; struct mcps802154_rx_frame_info info = { - .flags = MCPS802154_RX_INFO_TIMESTAMP_DTU, + .flags = MCPS802154_RX_FRAME_INFO_TIMESTAMP_DTU, }; llhw_rx_get_error_frame(local, &info); @@ -213,7 +213,7 @@ mcps802154_fproc_multi_rx_schedule_change(struct mcps802154_local *local) int frame_idx = local->fproc.frame_idx; struct mcps802154_access_frame *frame = &access->frames[frame_idx]; - if (frame->rx.info.timeout_dtu == -1) { + if (frame->rx.frame_config.timeout_dtu == -1) { /* Disable RX. */ int r = llhw_rx_disable(local); @@ -314,7 +314,8 @@ static int mcps802154_fproc_multi_handle_frame(struct mcps802154_local *local, frame = &access->frames[frame_idx]; if (!frame->is_tx) { - if (frame->rx.info.flags & MCPS802154_RX_INFO_AACK) + if (frame->rx.frame_config.flags & + MCPS802154_RX_FRAME_CONFIG_AACK) return -EINVAL; if (frame->sts_params) { @@ -323,14 +324,15 @@ static int mcps802154_fproc_multi_handle_frame(struct mcps802154_local *local, return r; } - r = llhw_rx_enable(local, &frame->rx.info, frame_idx, 0); + r = llhw_rx_enable(local, &frame->rx.frame_config, frame_idx, + 0); if (r) return r; mcps802154_fproc_change_state(local, &mcps802154_fproc_multi_rx); } else { - if (frame->tx_frame_info.rx_enable_after_tx_dtu) + if (frame->tx_frame_config.rx_enable_after_tx_dtu) return -EINVAL; skb = access->ops->tx_get_frame(access, frame_idx); @@ -347,8 +349,8 @@ static int mcps802154_fproc_multi_handle_frame(struct mcps802154_local *local, } } - r = llhw_tx_frame(local, skb, &frame->tx_frame_info, frame_idx, - 0); + r = llhw_tx_frame(local, skb, &frame->tx_frame_config, + frame_idx, 0); if (r) { access->ops->tx_return( access, frame_idx, skb, @@ -396,5 +398,12 @@ int mcps802154_fproc_multi_handle(struct mcps802154_local *local, return r; } } + + if (access->hrp_uwb_params) { + r = llhw_set_hrp_uwb_params(local, access->hrp_uwb_params); + if (r) + return r; + } + return mcps802154_fproc_multi_handle_frame(local, access, 0); } diff --git a/mac/fproc_rx.c b/mac/fproc_rx.c index cfa2b7b..1e45a7c 100644 --- a/mac/fproc_rx.c +++ b/mac/fproc_rx.c @@ -113,11 +113,11 @@ int mcps802154_fproc_rx_handle(struct mcps802154_local *local, struct mcps802154_access *access) { int r; - struct mcps802154_rx_info rx_info = { - .flags = MCPS802154_RX_INFO_AACK, + struct mcps802154_rx_frame_config rx_config = { + .flags = MCPS802154_RX_FRAME_CONFIG_AACK, .timeout_dtu = -1, }; - r = llhw_rx_enable(local, &rx_info, 0, 0); + r = llhw_rx_enable(local, &rx_config, 0, 0); if (r) return r; diff --git a/mac/fproc_tx.c b/mac/fproc_tx.c index 53f1281..eba18f6 100644 --- a/mac/fproc_tx.c +++ b/mac/fproc_tx.c @@ -136,16 +136,21 @@ int mcps802154_fproc_tx_handle(struct mcps802154_local *local, struct mcps802154_access *access) { int r; + u8 ack_req; + struct mcps802154_tx_frame_config tx_config = {}; struct sk_buff *skb = access->ops->tx_get_frame(access, 0); - u8 ack_req = skb->data[0] & IEEE802154_FC_ACK_REQ; - struct mcps802154_tx_frame_info tx_info = { - .flags = 0, - .rx_enable_after_tx_dtu = - ack_req ? IEEE802154_AIFS_DURATION_SYMBOLS * - local->llhw.symbol_dtu : - 0, - }; - r = llhw_tx_frame(local, skb, &tx_info, 0, 0); + + if (!skb) + return -ENOMEM; + + ack_req = skb->data[0] & IEEE802154_FC_ACK_REQ; + if (ack_req) { + tx_config.rx_enable_after_tx_dtu = + IEEE802154_AIFS_DURATION_SYMBOLS * + local->llhw.symbol_dtu; + } + + r = llhw_tx_frame(local, skb, &tx_config, 0, 0); if (r) { access->ops->tx_return( access, 0, skb, diff --git a/mac/idle_region.c b/mac/idle_region.c new file mode 100644 index 0000000..e4f62c3 --- /dev/null +++ b/mac/idle_region.c @@ -0,0 +1,166 @@ +/* + * This file is part of the UWB stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see <http://www.gnu.org/licenses/>. + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. Please contact Qorvo to inquire about licensing terms. + */ + +#include "idle_region.h" +#include "trace.h" +#include <net/idle_region_nl.h> +#include <linux/errno.h> +#include <linux/limits.h> + +static struct mcps802154_region_ops idle_region_ops; + +struct idle_local { + /** + * @region: Region instance returned to MCPS. + */ + struct mcps802154_region region; + /** + * @llhw: Low-level device pointer. + */ + struct mcps802154_llhw *llhw; + /** + * @params: Parameters. + */ + struct idle_params params; + /** + * @access: Access returned to ca. + */ + struct mcps802154_access access; +}; + +static inline struct idle_local * +region_to_local(struct mcps802154_region *region) +{ + return container_of(region, struct idle_local, region); +} + +static struct mcps802154_region *idle_open(struct mcps802154_llhw *llhw) +{ + struct idle_local *local; + + local = kzalloc(sizeof(*local), GFP_KERNEL); + if (!local) + return NULL; + local->llhw = llhw; + local->region.ops = &idle_region_ops; + + /* Default value of parameters. */ + local->params.min_duration_dtu = llhw->anticip_dtu * 2; + local->params.max_duration_dtu = 0; + + return &local->region; +} + +static void idle_close(struct mcps802154_region *region) +{ + kfree(region_to_local(region)); +} + +static const struct nla_policy idle_param_nla_policy[IDLE_PARAM_ATTR_MAX + 1] = { + [IDLE_PARAM_ATTR_MIN_DURATION_DTU] = NLA_POLICY_MIN(NLA_S32, 0), + [IDLE_PARAM_ATTR_MAX_DURATION_DTU] = NLA_POLICY_MIN(NLA_S32, 0), +}; + +static int idle_set_parameters(struct mcps802154_region *region, + const struct nlattr *params, + struct netlink_ext_ack *extack) +{ + struct idle_local *local = region_to_local(region); + struct nlattr *attrs[IDLE_PARAM_ATTR_MAX + 1]; + struct idle_params *p = &local->params; + int min_duration_dtu, max_duration_dtu; + int r; + + r = nla_parse_nested(attrs, IDLE_PARAM_ATTR_MAX, params, + idle_param_nla_policy, extack); + if (r) + return r; + + min_duration_dtu = + attrs[IDLE_PARAM_ATTR_MIN_DURATION_DTU] ? + nla_get_s32(attrs[IDLE_PARAM_ATTR_MIN_DURATION_DTU]) : + p->min_duration_dtu; + max_duration_dtu = + attrs[IDLE_PARAM_ATTR_MAX_DURATION_DTU] ? + nla_get_s32(attrs[IDLE_PARAM_ATTR_MAX_DURATION_DTU]) : + p->max_duration_dtu; + if (max_duration_dtu && min_duration_dtu && + min_duration_dtu > max_duration_dtu) + return -EINVAL; + + p->min_duration_dtu = min_duration_dtu; + p->max_duration_dtu = max_duration_dtu; + trace_region_idle_params(p); + return 0; +} + +static struct mcps802154_access_ops idle_access_ops = {}; + +static struct mcps802154_access * +idle_get_access(struct mcps802154_region *region, u32 next_timestamp_dtu, + int next_in_region_dtu, int region_duration_dtu) +{ + struct idle_local *local = region_to_local(region); + struct mcps802154_access *access = &local->access; + const struct idle_params *p = &local->params; + int left_region_duration_dtu = region_duration_dtu - next_in_region_dtu; + int duration_dtu; + + if (!left_region_duration_dtu) { + /* Region used with endless scheduler. */ + duration_dtu = p->max_duration_dtu; + } else { + /* Region used directly in on_demand scheduler. */ + if (left_region_duration_dtu < p->min_duration_dtu) + return NULL; + duration_dtu = left_region_duration_dtu; + } + + trace_region_idle_get_access(next_timestamp_dtu, duration_dtu); + access->method = MCPS802154_ACCESS_METHOD_IDLE; + access->ops = &idle_access_ops; + access->timestamp_dtu = next_timestamp_dtu; + access->duration_dtu = duration_dtu; + + return access; +} + +static struct mcps802154_region_ops idle_region_ops = { + .owner = THIS_MODULE, + .name = "idle", + .open = idle_open, + .close = idle_close, + .set_parameters = idle_set_parameters, + .get_demand = NULL, /* Not wanted. */ + .get_access = idle_get_access, +}; + +int mcps802154_idle_region_init(void) +{ + return mcps802154_region_register(&idle_region_ops); +} + +void mcps802154_idle_region_exit(void) +{ + mcps802154_region_unregister(&idle_region_ops); +} diff --git a/mac/idle_region.h b/mac/idle_region.h new file mode 100644 index 0000000..f3082d1 --- /dev/null +++ b/mac/idle_region.h @@ -0,0 +1,42 @@ +/* + * This file is part of the UWB stack for linux. + * + * Copyright (c) 2021-2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see <http://www.gnu.org/licenses/>. + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. Please contact Qorvo to inquire about licensing terms. + */ + +#ifndef IDLE_REGION_H +#define IDLE_REGION_H + +struct idle_params { + /** + * @min_duration_dtu: Minimum duration of an access. + * If min is 0, no minimum is required on get_access. + */ + int min_duration_dtu; + /** + * @max_duration_dtu: Maximum duration of an access. + */ + int max_duration_dtu; +}; + +int mcps802154_idle_region_init(void); +void mcps802154_idle_region_exit(void); + +#endif /* IDLE_REGION_H */ diff --git a/mac/include/net/fira_region_nl.h b/mac/include/net/fira_region_nl.h index 789610b..21f7655 100644 --- a/mac/include/net/fira_region_nl.h +++ b/mac/include/net/fira_region_nl.h @@ -25,18 +25,18 @@ #define FIRA_REGION_NL_H /** - * enum fira_call - Fira calls identifiers. + * enum fira_call - FiRa calls identifiers. * * @FIRA_CALL_GET_CAPABILITIES: - * Request Fira capabilities. + * Request FiRa capabilities. * @FIRA_CALL_SESSION_INIT: - * Initialize Fira session. + * Initialize FiRa session. * @FIRA_CALL_SESSION_START: - * Start Fira session. + * Start FiRa session. * @FIRA_CALL_SESSION_STOP: - * Stop Fira session. + * Stop FiRa session. * @FIRA_CALL_SESSION_DEINIT: - * Deinit Fira session. + * Deinit FiRa session. * @FIRA_CALL_SESSION_SET_PARAMS: * Set session parameters. * @FIRA_CALL_NEW_CONTROLEE: @@ -76,7 +76,7 @@ enum fira_call { }; /** - * enum fira_capability_attrs - Fira capabilities. + * enum fira_capability_attrs - FiRa capabilities. * * @FIRA_CAPABILITY_ATTR_FIRA_PHY_VERSION_RANGE: * FiRa PHY version range supported, ex: 0x01010202 -> support from 1.1 to 2.2. @@ -142,6 +142,10 @@ enum fira_call { * 1 segment for STS supported. * @FIRA_CAPABILITY_ATTR_NUMBER_OF_STS_SEGMENTS_2: * 2 segments for STS supported. + * @FIRA_CAPABILITY_ATTR_NUMBER_OF_STS_SEGMENTS_3: + * 3 segments for STS supported. + * @FIRA_CAPABILITY_ATTR_NUMBER_OF_STS_SEGMENTS_4: + * 4 segments for STS supported. * @FIRA_CAPABILITY_ATTR_PSDU_DATA_RATE_6M81: * 6.81 Mbps support. * @FIRA_CAPABILITY_ATTR_PSDU_DATA_RATE_7M80: @@ -217,6 +221,8 @@ enum fira_capability_attrs { FIRA_CAPABILITY_ATTR_NUMBER_OF_STS_SEGMENTS_0, FIRA_CAPABILITY_ATTR_NUMBER_OF_STS_SEGMENTS_1, FIRA_CAPABILITY_ATTR_NUMBER_OF_STS_SEGMENTS_2, + FIRA_CAPABILITY_ATTR_NUMBER_OF_STS_SEGMENTS_3, + FIRA_CAPABILITY_ATTR_NUMBER_OF_STS_SEGMENTS_4, FIRA_CAPABILITY_ATTR_PSDU_DATA_RATE_6M81, FIRA_CAPABILITY_ATTR_PSDU_DATA_RATE_7M80, FIRA_CAPABILITY_ATTR_PSDU_DATA_RATE_27M2, @@ -243,7 +249,7 @@ enum fira_capability_attrs { }; /** - * enum fira_call_attrs - Fira call attributes. + * enum fira_call_attrs - FiRa call attributes. * * @FIRA_CALL_ATTR_SESSION_ID: * Session identifier. @@ -259,6 +265,10 @@ enum fira_capability_attrs { * Session state. * @FIRA_CALL_ATTR_SESSION_COUNT: * Sessions count. + * @FIRA_CALL_ATTR_SEQUENCE_NUMBER: + * Session notification counter. + * @FIRA_CALL_ATTR_RANGING_DIAGNOSTICS: + * Diagnostic information. * * @FIRA_CALL_ATTR_UNSPEC: Invalid command. * @__FIRA_CALL_ATTR_AFTER_LAST: Internal use. @@ -273,13 +283,15 @@ enum fira_call_attrs { FIRA_CALL_ATTR_CAPABILITIES, FIRA_CALL_ATTR_SESSION_STATE, FIRA_CALL_ATTR_SESSION_COUNT, + FIRA_CALL_ATTR_SEQUENCE_NUMBER, + FIRA_CALL_ATTR_RANGING_DIAGNOSTICS, __FIRA_CALL_ATTR_AFTER_LAST, FIRA_CALL_ATTR_MAX = __FIRA_CALL_ATTR_AFTER_LAST - 1 }; /** - * enum fira_session_param_attrs - Fira session parameters attributes. + * enum fira_session_param_attrs - FiRa session parameters attributes. * * @FIRA_SESSION_PARAM_ATTR_DEVICE_TYPE: * Controlee (0) or controller (1) @@ -325,21 +337,19 @@ enum fira_call_attrs { * @FIRA_SESSION_PARAM_ATTR_CHANNEL_NUMBER: * Override channel for this session: 5, 6, 8, 9, 10, 12, 13 or 14 * @FIRA_SESSION_PARAM_ATTR_PREAMBLE_CODE_INDEX: - * Override preamble code for this session, BPRF (9-24), - * HPRF (25-32, not supported) + * Override preamble code for this session, BPRF (9-24), HPRF (25-32) * @FIRA_SESSION_PARAM_ATTR_RFRAME_CONFIG: * SP0 (0), SP1 (1), SP2 (2, unused, not in FiRa 1.1) or SP3 (3, default) * @FIRA_SESSION_PARAM_ATTR_PRF_MODE: - * BPRF (0, default) or HPRF (1, not supported) + * BPRF (0, default), HPRF (1) or HPRF with high data rate (2) * @FIRA_SESSION_PARAM_ATTR_PREAMBLE_DURATION: * 64 (1, default) or 32 (0, only for HPRF) * @FIRA_SESSION_PARAM_ATTR_SFD_ID: - * BPRF (0 or 2), HPRF (1-4, not supported), default 2 + * BPRF (0 or 2), HPRF (1-4), default 2 * @FIRA_SESSION_PARAM_ATTR_NUMBER_OF_STS_SEGMENTS: * 0-2, default to 0 for SP0, default to 1 for SP1 & SP3, 2 not supported * @FIRA_SESSION_PARAM_ATTR_PSDU_DATA_RATE: - * 6.81 Mbps (0, default), 7.80 Mbps (1, not supported), - * 27.2 Mbps (2, not supported), 31.2 Mbps (3, not supported) + * 6.81 Mbps (0, default), 7.80 Mbps (1), 27.2 Mbps (2), 31.2 Mbps (3) * @FIRA_SESSION_PARAM_ATTR_BPRF_PHR_DATA_RATE: * 850 kbps (0, default) or 6.81 Mbps (1) * @FIRA_SESSION_PARAM_ATTR_MAC_FCS_TYPE: @@ -373,10 +383,32 @@ enum fira_call_attrs { * Report AoA elevation in result message, disabled (0, default) or enabled (1) * @FIRA_SESSION_PARAM_ATTR_REPORT_AOA_FOM: * Report AoA FOM in result message, disabled (0, default) or enabled (1) + * @FIRA_SESSION_PARAM_ATTR_REPORT_RSSI: + * Report average RSSI of the round in result message, disabled (0, default) or enabled (1) * @FIRA_SESSION_PARAM_ATTR_DATA_VENDOR_OUI: - * Set the vendor OUI for custom data exchanges - * @FIRA_SESSION_PARAM_ATTR_DATA_PAYLOAD: - * Set the data payload to send in next ranging packet +* Set the vendor OUI for custom data exchanges +* @FIRA_SESSION_PARAM_ATTR_DATA_PAYLOAD: +* Set the data payload to send in next ranging packet + * @FIRA_SESSION_PARAM_ATTR_DIAGNOSTICS: + * Report diagnostic information on each round, disabled (0, default) or enabled (1) + * @FIRA_SESSION_PARAM_ATTR_DIAGNOSTICS_FRAME_REPORTS_FIELDS: + * Bitfield activating various frame diagnostics in the report (0: no frame diagnostic report, default). + * see &enum fira_ranging_diagnostics_frame_report_flags + * @FIRA_SESSION_PARAM_ATTR_STS_LENGTH: + * Number of symbols in a STS segment. 32 (0x00), 64 (0x01, default) or 128 + * symbols (0x02) + * @FIRA_SESSION_PARAM_ATTR_CAP_SIZE_MAX: + * Maximum for contention access period size + * @FIRA_SESSION_PARAM_ATTR_CAP_SIZE_MIN: + * Minimum for contention access period size + * @FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_CONFIG: + * Configure range data notification + * @FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_PROXIMITY_NEAR: + * Lower bound in cm above which the ranging notifications + * should be enabled when RANGE_DATA_NTF_CONFIG is set to "proximity" + * @FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_PROXIMITY_FAR: + * Upper bound in cm above which the ranging notifications + * should be disabled when RANGE_DATA_NTF_CONFIG is set to "proximity" * * @FIRA_SESSION_PARAM_ATTR_UNSPEC: Invalid command. * @__FIRA_SESSION_PARAM_ATTR_AFTER_LAST: Internal use. @@ -434,16 +466,28 @@ enum fira_session_param_attrs { FIRA_SESSION_PARAM_ATTR_REPORT_AOA_AZIMUTH, FIRA_SESSION_PARAM_ATTR_REPORT_AOA_ELEVATION, FIRA_SESSION_PARAM_ATTR_REPORT_AOA_FOM, + FIRA_SESSION_PARAM_ATTR_REPORT_RSSI, /* Custom Data */ FIRA_SESSION_PARAM_ATTR_DATA_VENDOR_OUI, FIRA_SESSION_PARAM_ATTR_DATA_PAYLOAD, - + /* Diagnostics */ + FIRA_SESSION_PARAM_ATTR_DIAGNOSTICS, + FIRA_SESSION_PARAM_ATTR_DIAGNOSTICS_FRAME_REPORTS_FIELDS, + /* Misc */ + FIRA_SESSION_PARAM_ATTR_STS_LENGTH, + /* Contention-based ranging */ + FIRA_SESSION_PARAM_ATTR_CAP_SIZE_MAX, + FIRA_SESSION_PARAM_ATTR_CAP_SIZE_MIN, + /* Range data notification enable */ + FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_CONFIG, + FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_PROXIMITY_NEAR, + FIRA_SESSION_PARAM_ATTR_RANGE_DATA_NTF_PROXIMITY_FAR, __FIRA_SESSION_PARAM_ATTR_AFTER_LAST, FIRA_SESSION_PARAM_ATTR_MAX = __FIRA_SESSION_PARAM_ATTR_AFTER_LAST - 1 }; /** - * enum fira_call_controlee_attrs - Fira controlee parameters attributes. + * enum fira_call_controlee_attrs - FiRa controlee parameters attributes. * * @FIRA_CALL_CONTROLEE_ATTR_SHORT_ADDR: * Controlee short address. @@ -485,7 +529,7 @@ enum fira_ranging_data_attrs_stopped_values { }; /** - * enum fira_ranging_data_attrs - Fira ranging data attributes. + * enum fira_ranging_data_attrs - FiRa ranging data attributes. * * @FIRA_RANGING_DATA_ATTR_STOPPED: * If present, session was stopped, see @@ -517,7 +561,7 @@ enum fira_ranging_data_attrs { }; /** - * enum fira_ranging_data_measurements_attrs - Fira ranging data measurements + * enum fira_ranging_data_measurements_attrs - FiRa ranging data measurements * attributes. * * @FIRA_RANGING_DATA_MEASUREMENTS_ATTR_SHORT_ADDR: @@ -553,6 +597,10 @@ enum fira_ranging_data_attrs { * Estimation of azimuth reliability of the participing device. * @FIRA_RANGING_DATA_MEASUREMENTS_ATTR_REMOTE_AOA_ELEVATION_FOM: * Estimation of elevation reliability of the participing device. + * @FIRA_RANGING_DATA_MEASUREMENTS_ATTR_RSSI: + * RSSI "summary" for received frames during the ranging round, + * reported as Q7.1. Summary method depends on session params + * (average, minimum, etc). * @FIRA_RANGING_DATA_MEASUREMENTS_ATTR_DATA_PAYLOAD_SEQ_SENT: * Sequence number of last data sent * @FIRA_RANGING_DATA_MEASUREMENTS_ATTR_DATA_PAYLOAD_RECV: @@ -578,6 +626,7 @@ enum fira_ranging_data_measurements_attrs { FIRA_RANGING_DATA_MEASUREMENTS_ATTR_REMOTE_AOA_ELEVATION_PI, FIRA_RANGING_DATA_MEASUREMENTS_ATTR_REMOTE_AOA_AZIMUTH_FOM, FIRA_RANGING_DATA_MEASUREMENTS_ATTR_REMOTE_AOA_ELEVATION_FOM, + FIRA_RANGING_DATA_MEASUREMENTS_ATTR_RSSI, FIRA_RANGING_DATA_MEASUREMENTS_ATTR_DATA_PAYLOAD_SEQ_SENT, FIRA_RANGING_DATA_MEASUREMENTS_ATTR_DATA_PAYLOAD_RECV, @@ -587,7 +636,7 @@ enum fira_ranging_data_measurements_attrs { }; /** - * enum fira_ranging_data_measurements_aoa_attrs - Fira ranging AoA measurements + * enum fira_ranging_data_measurements_aoa_attrs - FiRa ranging AoA measurements * attributes. * * @FIRA_RANGING_DATA_MEASUREMENTS_AOA_ATTR_RX_ANTENNA_SET: @@ -616,14 +665,14 @@ enum fira_ranging_data_measurements_aoa_attrs { }; /** - * enum fira_session_param_meas_seq_step_attrs - Fira measurement sequence + * enum fira_session_param_meas_seq_step_attrs - FiRa measurement sequence * step attributes. * * @FIRA_SESSION_PARAM_MEAS_SEQ_STEP_ATTR_MEASUREMENT_TYPE: * The type of measurement to perform during the step. * @FIRA_SESSION_PARAM_MEAS_SEQ_STEP_ATTR_N_MEASUREMENTS: * The number of times this type of measurement shall be performed - * during the step. + * during the step. * @FIRA_SESSION_PARAM_MEAS_SEQ_STEP_ATTR_RX_ANT_SET_NONRANGING: * The antenna set to use to receive the non-rfames during the step. * @FIRA_SESSION_PARAM_MEAS_SEQ_STEP_ATTR_RX_ANT_SETS_RANGING: @@ -654,11 +703,11 @@ enum fira_session_param_meas_seq_step_attrs { /** * enum fira_session_params_meas_seq_step_sets_attrs - Attributes of the - * Fira RX antenna sets to use during a step. + * FiRa RX antenna sets to use during a step. * * @FIRA_SESSION_PARAM_MEAS_SEQ_STEP_RX_ANT_SETS_RANGING_ATTR_0: * Antenna set used to receive all rframes for range, azimuth and elevation - * steps or initial rframe for azimuth_elevation step. + * steps or initial rframe for azimuth_elevation step. * @FIRA_SESSION_PARAM_MEAS_SEQ_STEP_RX_ANT_SETS_RANGING_ATTR_1: * Antenna set used to receive final rframes for azimuth_elevation step. * @@ -680,4 +729,140 @@ enum fira_session_params_meas_seq_step_sets_attrs { 1 }; +/** + * enum fira_ranging_diagnostics_attrs - FiRa ranging diagnostic attributes. + * + * @FIRA_RANGING_DIAGNOSTICS_ATTR_FRAME_REPORTS: + * Diagnostics for individual frames of the round. + * + * @FIRA_RANGING_DIAGNOSTICS_ATTR_UNSPEC: Invalid command. + * @__FIRA_RANGING_DIAGNOSTICS_ATTR_AFTER_LAST : Internal use. + * @FIRA_RANGING_DIAGNOSTICS_ATTR_MAX : Internal use. + */ +enum fira_ranging_diagnostics_attrs { + FIRA_RANGING_DIAGNOSTICS_ATTR_UNSPEC, + FIRA_RANGING_DIAGNOSTICS_ATTR_FRAME_REPORTS, + + __FIRA_RANGING_DIAGNOSTICS_ATTR_AFTER_LAST, + FIRA_RANGING_DIAGNOSTICS_ATTR_MAX = + __FIRA_RANGING_DIAGNOSTICS_ATTR_AFTER_LAST - 1 +}; + +/** + * enum fira_ranging_diagnostics_frame_reports_attrs - FiRa ranging + * diagnostic info for individual frames. + * + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_ANT_SET: + * Antenna set ID, used for the frame transmission. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_ACTION: + * Action type of the frame (0: TX or 1: RX). + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_MSG_ID: + * FiRa message ID (0: RIM, 1: RRM, 2: RFM, 3: CM, + * 4: MRM, 5: RRRM, 6: CU). + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_RSSIS: + * RSSI for the current (Rx) frame, reported as a Q7.1. + * As many values as receivers. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_AOAS: + * Nested attribute reporting different AoA related information. + * As many as AoA types. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_CIRS: + * Nested attribute reporting CIR sample window information. + * As many array elements as receivers. + * + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_UNSPEC: Invalid command. + * @__FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_AFTER_LAST: Internal use. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_MAX: Internal use. + */ +enum fira_ranging_diagnostics_frame_reports_attrs { + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_UNSPEC, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_ANT_SET, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_ACTION, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_MSG_ID, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_RSSIS, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_AOAS, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_CIRS, + + __FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_AFTER_LAST, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_MAX = + __FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_ATTR_AFTER_LAST - 1 +}; + +/** + * enum fira_ranging_diagnostics_frame_reports_aoa_attrs - AoA diagnostic + * information per frame + * + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_AOAS_ATTR_TDOA: + * TDoA in rctu, reported as s16. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_AOAS_ATTR_PDOA: + * PDoA in radians, reported as Q5.11. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_AOAS_ATTR_AOA: + * AoA in radians, reported as Q5.11. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_AOAS_ATTR_FOM: + * AoA FoM between 0 and 255 (0 being an invalid measure and 255 being + * a 100% confidence measure), reported as u8. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_AOAS_ATTR_TYPE: + * AoA Measurement type (azimuth, elevation...), reported as u8. + * + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_AOAS_ATTR_UNSPEC: Invalid command. + * @__FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_AOAS_ATTR_AFTER_LAST: Internal use. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_AOAS_ATTR_MAX: Internal use. + */ +enum fira_ranging_diagnostics_frame_reports_aoa_attrs { + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_AOAS_ATTR_UNSPEC, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_AOAS_ATTR_TDOA, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_AOAS_ATTR_PDOA, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_AOAS_ATTR_AOA, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_AOAS_ATTR_FOM, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_AOAS_ATTR_TYPE, + + __FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_AOAS_ATTR_AFTER_LAST, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_AOAS_ATTR_MAX = + __FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_AOAS_ATTR_AFTER_LAST - + 1 +}; + +/** + * enum fira_ranging_diagnostics_frame_reports_cir_attrs - CIR diagnostic + * information per frame + * + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_FP_IDX: + * Absolute index of the sample considered as first path, reported as u16. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_FP_SNR: + * SNR of the sample considered as first path, reported as s16. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_FP_NS: + * Timestamp of the sample considered as first path, reported as u16. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_PP_IDX: + * Absolute index of the sample considered as peak path, reported as u16. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_PP_SNR: + * SNR of the sample considered as peak path, reported as s16. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_PP_NS: + * Timestamp of the sample considered as peak path, reported as u16. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_FP_SAMPLE_OFFSET: + * Offset of the first path in the sample window, reported as u16. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_FP_SAMPLE_WINDOW: + * Sliding window containing CIR samples, each sample is considered as + * a byte sequence depending on sample size. + * As many samples as the window size. + * + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_UNSPEC: Invalid command. + * @__FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_AFTER_LAST: Internal use. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_MAX: Internal use. + */ +enum fira_ranging_diagnostics_frame_reports_cir_attrs { + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_UNSPEC, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_FP_IDX, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_FP_SNR, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_FP_NS, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_PP_IDX, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_PP_SNR, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_PP_NS, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_FP_SAMPLE_OFFSET, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_FP_SAMPLE_WINDOW, + + __FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_AFTER_LAST, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_MAX = + __FIRA_RANGING_DIAGNOSTICS_FRAME_REPORTS_CIRS_ATTR_AFTER_LAST - + 1 +}; + #endif /* FIRA_REGION_NL_H */ diff --git a/mac/include/net/fira_region_params.h b/mac/include/net/fira_region_params.h index e6fe8ad..a5bebaa 100644 --- a/mac/include/net/fira_region_params.h +++ b/mac/include/net/fira_region_params.h @@ -29,7 +29,7 @@ #define FIRA_VUPPER64_SIZE 8 #define FIRA_KEY_SIZE_MAX 32 #define FIRA_KEY_SIZE_MIN 16 -#define FIRA_CONTROLEES_MAX 16 +#define FIRA_CONTROLEES_MAX 8 #define FIRA_RX_ANTENNA_PAIR_INVALID 0xff /* * In BPRF, frame is at most 127 @@ -37,6 +37,9 @@ */ #define FIRA_DATA_PAYLOAD_SIZE_MAX 84 +/* From UCI spec v1.1.0 (converted to mm) */ +#define FIRA_RANGE_DATA_NTF_PROXIMITY_FAR_DEFAULT 200000 + /** * enum fira_device_type - Type of a device. * @FIRA_DEVICE_TYPE_CONTROLEE: The device is a controlee. @@ -138,16 +141,18 @@ enum fira_rframe_config { }; /** - * enum fira_prf_mode - **[NOT IMPLEMENTED]** Pulse Repetition Frequency - * mode. + * enum fira_prf_mode - Pulse Repetition Frequency mode * @FIRA_PRF_MODE_BPRF: Base Pulse Repetition Frequency. * @FIRA_PRF_MODE_HPRF: Higher Pulse Repetition Frequency. + * @FIRA_PRF_MODE_HPRF_HIGH_RATE: Higher Pulse Repetition Frequency allows + * high data rate (27.2 Mbps and 31.2 Mbps). * * This enum is not used in the current implementation. */ enum fira_prf_mode { FIRA_PRF_MODE_BPRF, FIRA_PRF_MODE_HPRF, + FIRA_PRF_MODE_HPRF_HIGH_RATE, }; /** @@ -180,17 +185,19 @@ enum fira_sfd_id { }; /** - * enum fira_sts_segments - **[NOT IMPLEMENTED]** RFU. - * @FIRA_STS_SEGMENTS_0: RFU. - * @FIRA_STS_SEGMENTS_1: RFU. - * @FIRA_STS_SEGMENTS_2: RFU. - * - * This enum is not used in the current implementation. + * enum fira_sts_segments - Number of STS segments. + * @FIRA_STS_SEGMENTS_0: No STS Segment (Rframe config SP0). + * @FIRA_STS_SEGMENTS_1: 1 STS Segment. + * @FIRA_STS_SEGMENTS_2: 2 STS Segments. + * @FIRA_STS_SEGMENTS_3: 3 STS Segments. + * @FIRA_STS_SEGMENTS_4: 4 STS Segments. */ enum fira_sts_segments { FIRA_STS_SEGMENTS_0, FIRA_STS_SEGMENTS_1, FIRA_STS_SEGMENTS_2, + FIRA_STS_SEGMENTS_3, + FIRA_STS_SEGMENTS_4, }; /** @@ -208,15 +215,14 @@ enum fira_psdu_data_rate { }; /** - * enum fira_phr_data_rate - **[NOT IMPLEMENTED]** - * Data rate used to exchange PHR. - * @FIRA_PHR_DATA_RATE_850k: 850kb/s rate. + * enum fira_phr_data_rate - Data rate used to exchange PHR. + * @FIRA_PHR_DATA_RATE_850K: 850kb/s rate. * @FIRA_PHR_DATA_RATE_6M81: 6.8Mb/s rate. * * This enum is not used in the current implementation. */ enum fira_phr_data_rate { - FIRA_PHR_DATA_RATE_850k, + FIRA_PHR_DATA_RATE_850K, FIRA_PHR_DATA_RATE_6M81, }; @@ -231,6 +237,19 @@ enum fira_mac_fcs_type { }; /** + * enum fira_rssi_report_type - Mode used to sum up individual frames RSSI + * in report. + * @FIRA_RSSI_REPORT_NONE: No RSSI value in report. + * @FIRA_RSSI_REPORT_MINIMUM: Report minimum RSSI + * @FIRA_RSSI_REPORT_AVERAGE: Report average RSSI + */ +enum fira_rssi_report_type { + FIRA_RSSI_REPORT_NONE, + FIRA_RSSI_REPORT_MINIMUM, + FIRA_RSSI_REPORT_AVERAGE, +}; + +/** * enum fira_sts_config - Scrambled Timestamp Sequence configuration. * @FIRA_STS_CONFIG_STATIC: Use a static STS configuration. * @FIRA_STS_CONFIG_DYNAMIC: Use a dynamic STS configuration. @@ -308,4 +327,45 @@ enum fira_measurement_type { __FIRA_MEASUREMENT_TYPE_AFTER_LAST, }; +/** + * enum fira_ranging_diagnostics_frame_report_flags - Activation flags for different frame diagnostics information. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORT_NONE: No specific frame diagnostic report requested. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORT_RSSIS: Report RSSI in frame diagnostics. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORT_AOAS: Report AOA in frame diagnostics. + * @FIRA_RANGING_DIAGNOSTICS_FRAME_REPORT_CIRS: Report CIR in frame diagnostics. + * @__FIRA_RANGING_DIAGNOSTICS_FRAME_REPORT_AFTER_LAST: Internal use. + */ +enum fira_ranging_diagnostics_frame_report_flags { + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORT_NONE = 0, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORT_RSSIS = 1 << 0, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORT_AOAS = 1 << 1, + FIRA_RANGING_DIAGNOSTICS_FRAME_REPORT_CIRS = 1 << 2, + __FIRA_RANGING_DIAGNOSTICS_FRAME_REPORT_AFTER_LAST = 1U << 31, +}; + +/** + * enum fira_sts_length - Number of symbols in a STS segment. + * @FIRA_STS_LENGTH_32: The STS length is 32 symbols. + * @FIRA_STS_LENGTH_64: The STS length is 64 symbols. + * @FIRA_STS_LENGTH_128: The STS length is 128 symbols. + */ +enum fira_sts_length { + FIRA_STS_LENGTH_32 = 0, + FIRA_STS_LENGTH_64 = 1, + FIRA_STS_LENGTH_128 = 2, +}; + +/** + * enum fira_range_data_ntf_config - Configure range data notification. + * @FIRA_RANGE_DATA_NTF_DISABLED: Do not report range data. + * @FIRA_RANGE_DATA_NTF_ALWAYS: Report range data. + * @FIRA_RANGE_DATA_NTF_PROXIMITY: Report range data if it is within range + * defined by proximity parameters (RANGE_DATA_NTF_PROXIMITY_NEAR/FAR). + */ +enum fira_range_data_ntf_config { + FIRA_RANGE_DATA_NTF_DISABLED = 0, + FIRA_RANGE_DATA_NTF_ALWAYS = 1, + FIRA_RANGE_DATA_NTF_PROXIMITY = 2 +}; + #endif /* NET_FIRA_REGION_PARAMS_H */ diff --git a/mac/include/net/idle_region_nl.h b/mac/include/net/idle_region_nl.h new file mode 100644 index 0000000..03a7e05 --- /dev/null +++ b/mac/include/net/idle_region_nl.h @@ -0,0 +1,49 @@ +/* + * This file is part of the UWB stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see <http://www.gnu.org/licenses/>. + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. Please contact Qorvo to inquire about licensing terms. + */ + +#ifndef IDLE_REGION_NL_H +#define IDLE_REGION_NL_H + +/** + * enum idle_param_attrs - Idle parameters attributes. + * + * @IDLE_PARAM_ATTR_MIN_DURATION_DTU: + * Minimum duration of an access. + * @IDLE_PARAM_ATTR_MAX_DURATION_DTU: + * Maximum duration of an access. + * + * @IDLE_PARAM_ATTR_UNSPEC: Invalid command. + * @__IDLE_PARAM_ATTR_AFTER_LAST: Internal use. + * @IDLE_PARAM_ATTR_MAX: Internal use. + */ +enum idle_param_attrs { + IDLE_PARAM_ATTR_UNSPEC, + + IDLE_PARAM_ATTR_MIN_DURATION_DTU, + IDLE_PARAM_ATTR_MAX_DURATION_DTU, + + __IDLE_PARAM_ATTR_AFTER_LAST, + IDLE_PARAM_ATTR_MAX = __IDLE_PARAM_ATTR_AFTER_LAST - 1 +}; + +#endif /* IDLE_REGION_NL_H */ diff --git a/mac/include/net/mcps802154.h b/mac/include/net/mcps802154.h index dd9c8dc..77ef002 100644 --- a/mac/include/net/mcps802154.h +++ b/mac/include/net/mcps802154.h @@ -35,6 +35,12 @@ /** Maximum number of STS segments. */ #define MCPS802154_STS_N_SEGS_MAX 4 +/** Maximum number of RSSI values. */ +#define MCPS802154_RSSIS_N_MAX 2 + +/** Maximum number of angle of arrival measurements. */ +#define MCPS802154_RX_AOA_MEASUREMENTS_MAX 3 + /** * struct mcps802154_channel - Channel parameters. */ @@ -61,10 +67,125 @@ struct mcps802154_channel { * Support for ranging (RDEV). TODO: move to &ieee802154_hw. * @MCPS802154_LLHW_ERDEV: * Support for enhanced ranging (ERDEV). TODO: move to &ieee802154_hw. + * @MCPS802154_LLHW_BPRF: + * Support for BPRF. + * @MCPS802154_LLHW_HPRF: + * Support for HPRF. + * @MCPS802154_LLHW_DATA_RATE_850K: + * Support for data rate 110 kpbs. + * @MCPS802154_LLHW_DATA_RATE_6M81: + * Support for data rate 6.81 Mpbs. + * @MCPS802154_LLHW_DATA_RATE_7M80: + * Support for data rate 7.8 Mpbs. + * @MCPS802154_LLHW_DATA_RATE_27M2: + * Support for data rate 27.2 Mpbs. + * @MCPS802154_LLHW_DATA_RATE_31M2: + * Support for data rate 31.2 Mpbs. + * @MCPS802154_LLHW_DATA_RATE_CUSTOM: + * Support for custom data rate, When presents extra data rate are + * possible to set. + * @MCPS802154_LLHW_PHR_DATA_RATE_850K: + * Support PHR data rate 850 kpbs. + * @MCPS802154_LLHW_PHR_DATA_RATE_6M81: + * Support PHR data rate 6.81 Mpbs. + * @MCPS802154_LLHW_PRF_4: + * Support Pulse Repetition Frequency 4 MHz. + * @MCPS802154_LLHW_PRF_16: + * Support Pulse Repetition Frequency 16 MHz. + * @MCPS802154_LLHW_PRF_64: + * Support Pulse Repetition Frequency 64 MHz. + * @MCPS802154_LLHW_PRF_125: + * Support Pulse Repetition Frequency 125 MHz. + * @MCPS802154_LLHW_PRF_250: + * Support Pulse Repetition Frequency 250 MHz. + * @MCPS802154_LLHW_PSR_16: + * Support 16 symbols in preamble symbol repetitions. + * @MCPS802154_LLHW_PSR_24: + * Support 24 symbols in preamble symbol repetitions. + * @MCPS802154_LLHW_PSR_32: + * Support 32 symbols in preamble symbol repetitions. + * @MCPS802154_LLHW_PSR_48: + * Support 48 symbols in preamble symbol repetitions. + * @MCPS802154_LLHW_PSR_64: + * Support 64 symbols in preamble symbol repetitions. + * @MCPS802154_LLHW_PSR_96: + * Support 96 symbols in preamble symbol repetitions. + * @MCPS802154_LLHW_PSR_128: + * Support 128 symbols in preamble symbol repetitions. + * @MCPS802154_LLHW_PSR_256: + * Support 256 symbols in preamble symbol repetitions. + * @MCPS802154_LLHW_PSR_1024: + * Support 1024 symbols in preamble symbol repetitions. + * @MCPS802154_LLHW_PSR_4096: + * Support 4096 symbols in preamble symbol repetitions. + * @MCPS802154_LLHW_SFD_4A: + * Support SFD defined in 4a. + * @MCPS802154_LLHW_SFD_4Z_4: + * Support SFD defined in 4z with length of 4 symbols. + * @MCPS802154_LLHW_SFD_4Z_8: + * Support SFD defined in 4z with length of 8 symbols. + * @MCPS802154_LLHW_SFD_4Z_16: + * Support SFD defined in 4z with length of 16 symbols. + * @MCPS802154_LLHW_SFD_4Z_32: + * Support SFD defined in 4z with length of 32 symbols. + * @MCPS802154_LLHW_STS_SEGMENT_1: + * Support one STS segment. + * @MCPS802154_LLHW_STS_SEGMENT_2: + * Support two STS segments. + * @MCPS802154_LLHW_STS_SEGMENT_3: + * Support three STS segments. + * @MCPS802154_LLHW_STS_SEGMENT_4: + * Support four STS segments. + * @MCPS802154_LLHW_AOA_AZIMUTH: + * Support AOA azimuth [-90°,+90°]. + * @MCPS802154_LLHW_AOA_AZIMUTH_FULL: + * Support AOA full azimuth [-180°,+180°]. + * @MCPS802154_LLHW_AOA_ELEVATION: + * Support AOA elevation [-90°,+90°]. + * @MCPS802154_LLHW_AOA_FOM: + * Support AOA figure of merit. */ enum mcps802154_llhw_flags { MCPS802154_LLHW_RDEV = BIT(0), MCPS802154_LLHW_ERDEV = BIT(1), + MCPS802154_LLHW_BPRF = BIT(2), + MCPS802154_LLHW_HPRF = BIT(3), + MCPS802154_LLHW_DATA_RATE_850K = BIT(4), + MCPS802154_LLHW_DATA_RATE_6M81 = BIT(5), + MCPS802154_LLHW_DATA_RATE_7M80 = BIT(6), + MCPS802154_LLHW_DATA_RATE_27M2 = BIT(7), + MCPS802154_LLHW_DATA_RATE_31M2 = BIT(8), + MCPS802154_LLHW_DATA_RATE_CUSTOM = BIT(9), + MCPS802154_LLHW_PHR_DATA_RATE_850K = BIT(10), + MCPS802154_LLHW_PHR_DATA_RATE_6M81 = BIT(11), + MCPS802154_LLHW_PRF_4 = BIT(12), + MCPS802154_LLHW_PRF_16 = BIT(13), + MCPS802154_LLHW_PRF_64 = BIT(14), + MCPS802154_LLHW_PRF_125 = BIT(15), + MCPS802154_LLHW_PRF_250 = BIT(16), + MCPS802154_LLHW_PSR_16 = BIT(17), + MCPS802154_LLHW_PSR_24 = BIT(18), + MCPS802154_LLHW_PSR_32 = BIT(19), + MCPS802154_LLHW_PSR_48 = BIT(20), + MCPS802154_LLHW_PSR_64 = BIT(21), + MCPS802154_LLHW_PSR_96 = BIT(22), + MCPS802154_LLHW_PSR_128 = BIT(23), + MCPS802154_LLHW_PSR_256 = BIT(24), + MCPS802154_LLHW_PSR_1024 = BIT(25), + MCPS802154_LLHW_PSR_4096 = BIT(26), + MCPS802154_LLHW_SFD_4A = BIT(27), + MCPS802154_LLHW_SFD_4Z_4 = BIT(28), + MCPS802154_LLHW_SFD_4Z_8 = BIT(29), + MCPS802154_LLHW_SFD_4Z_16 = BIT(30), + MCPS802154_LLHW_SFD_4Z_32 = BIT(31), + MCPS802154_LLHW_STS_SEGMENT_1 = BIT_ULL(32), + MCPS802154_LLHW_STS_SEGMENT_2 = BIT_ULL(33), + MCPS802154_LLHW_STS_SEGMENT_3 = BIT_ULL(34), + MCPS802154_LLHW_STS_SEGMENT_4 = BIT_ULL(35), + MCPS802154_LLHW_AOA_AZIMUTH = BIT_ULL(36), + MCPS802154_LLHW_AOA_AZIMUTH_FULL = BIT_ULL(37), + MCPS802154_LLHW_AOA_ELEVATION = BIT_ULL(38), + MCPS802154_LLHW_AOA_FOM = BIT_ULL(39), }; /** @@ -131,7 +252,7 @@ struct mcps802154_llhw { /** * @flags: Low-level hardware flags, see &enum mcps802154_llhw_flags. */ - u32 flags; + u64 flags; /** * @hw: Pointer to IEEE802154 hardware exposed by MCPS. The low-level * driver needs to update this and hw->phy according to supported @@ -153,57 +274,61 @@ struct mcps802154_llhw { * @priv: Driver private data. */ void *priv; + /** + * @rx_ctx_size: size of the context. + */ + u32 rx_ctx_size; }; /** - * enum mcps802154_tx_frame_info_flags - Flags for transmitting a frame. - * @MCPS802154_TX_FRAME_TIMESTAMP_DTU: + * enum mcps802154_tx_frame_config_flags - Flags for transmitting a frame. + * @MCPS802154_TX_FRAME_CONFIG_TIMESTAMP_DTU: * Start transmission at given timestamp in device time unit. - * @MCPS802154_TX_FRAME_CCA: + * @MCPS802154_TX_FRAME_CONFIG_CCA: * Use CCA before transmission using the programmed mode. - * @MCPS802154_TX_FRAME_RANGING: + * @MCPS802154_TX_FRAME_CONFIG_RANGING: * Enable precise timestamping for the transmitted frame and its response * (RDEV only). - * @MCPS802154_TX_FRAME_KEEP_RANGING_CLOCK: + * @MCPS802154_TX_FRAME_CONFIG_KEEP_RANGING_CLOCK: * Request that the ranging clock be kept valid after the transmission of * this frame (RDEV only). - * @MCPS802154_TX_FRAME_RANGING_PDOA: + * @MCPS802154_TX_FRAME_CONFIG_RANGING_PDOA: * Enable phase difference of arrival measurement for the response frame * (RDEV only). - * @MCPS802154_TX_FRAME_SP1: + * @MCPS802154_TX_FRAME_CONFIG_SP1: * Enable STS for the transmitted frame and its response, mode 1 (STS after * SFD and before PHR, ERDEV only). - * @MCPS802154_TX_FRAME_SP2: + * @MCPS802154_TX_FRAME_CONFIG_SP2: * Enable STS for the transmitted frame and its response, mode 2 (STS after * the payload, ERDEV only). - * @MCPS802154_TX_FRAME_SP3: + * @MCPS802154_TX_FRAME_CONFIG_SP3: * Enable STS for the transmitted frame and its response, mode 3 (STS after * SFD, no PHR, no payload, ERDEV only). - * @MCPS802154_TX_FRAME_STS_MODE_MASK: + * @MCPS802154_TX_FRAME_CONFIG_STS_MODE_MASK: * Mask covering all the STS mode configuration values. - * @MCPS802154_TX_FRAME_RANGING_ROUND: + * @MCPS802154_TX_FRAME_CONFIG_RANGING_ROUND: * Inform low-level driver the transmitted frame is the start of a ranging * round (RDEV only). * * If no timestamp flag is given, transmit as soon as possible. */ -enum mcps802154_tx_frame_info_flags { - MCPS802154_TX_FRAME_TIMESTAMP_DTU = BIT(0), - MCPS802154_TX_FRAME_CCA = BIT(1), - MCPS802154_TX_FRAME_RANGING = BIT(2), - MCPS802154_TX_FRAME_KEEP_RANGING_CLOCK = BIT(3), - MCPS802154_TX_FRAME_RANGING_PDOA = BIT(4), - MCPS802154_TX_FRAME_SP1 = BIT(5), - MCPS802154_TX_FRAME_SP2 = BIT(6), - MCPS802154_TX_FRAME_SP3 = BIT(5) | BIT(6), - MCPS802154_TX_FRAME_STS_MODE_MASK = BIT(5) | BIT(6), - MCPS802154_TX_FRAME_RANGING_ROUND = BIT(7), +enum mcps802154_tx_frame_config_flags { + MCPS802154_TX_FRAME_CONFIG_TIMESTAMP_DTU = BIT(0), + MCPS802154_TX_FRAME_CONFIG_CCA = BIT(1), + MCPS802154_TX_FRAME_CONFIG_RANGING = BIT(2), + MCPS802154_TX_FRAME_CONFIG_KEEP_RANGING_CLOCK = BIT(3), + MCPS802154_TX_FRAME_CONFIG_RANGING_PDOA = BIT(4), + MCPS802154_TX_FRAME_CONFIG_SP1 = BIT(5), + MCPS802154_TX_FRAME_CONFIG_SP2 = BIT(6), + MCPS802154_TX_FRAME_CONFIG_SP3 = BIT(5) | BIT(6), + MCPS802154_TX_FRAME_CONFIG_STS_MODE_MASK = BIT(5) | BIT(6), + MCPS802154_TX_FRAME_CONFIG_RANGING_ROUND = BIT(7), }; /** - * struct mcps802154_tx_frame_info - Information for transmitting a frame. + * struct mcps802154_tx_frame_config - Information for transmitting a frame. */ -struct mcps802154_tx_frame_info { +struct mcps802154_tx_frame_config { /** * @timestamp_dtu: If timestamped, date of transmission start. */ @@ -221,7 +346,7 @@ struct mcps802154_tx_frame_info { */ int rx_enable_after_tx_timeout_dtu; /** - * @flags: See &enum mcps802154_tx_frame_info_flags. + * @flags: See &enum mcps802154_tx_frame_config_flags. */ u8 flags; /** @@ -231,52 +356,52 @@ struct mcps802154_tx_frame_info { }; /** - * enum mcps802154_rx_info_flags - Flags for enabling the receiver. - * @MCPS802154_RX_INFO_TIMESTAMP_DTU: + * enum mcps802154_rx_frame_config_flags - Flags for enabling the receiver. + * @MCPS802154_RX_FRAME_CONFIG_TIMESTAMP_DTU: * Enable receiver at given timestamp in device time unit. - * @MCPS802154_RX_INFO_AACK: + * @MCPS802154_RX_FRAME_CONFIG_AACK: * Enable automatic acknowledgment. - * @MCPS802154_RX_INFO_RANGING: + * @MCPS802154_RX_FRAME_CONFIG_RANGING: * Enable precise timestamping for the received frame (RDEV only). - * @MCPS802154_RX_INFO_KEEP_RANGING_CLOCK: + * @MCPS802154_RX_FRAME_CONFIG_KEEP_RANGING_CLOCK: * Request that the ranging clock be kept valid after the reception of the * frame (RDEV only). - * @MCPS802154_RX_INFO_RANGING_PDOA: + * @MCPS802154_RX_FRAME_CONFIG_RANGING_PDOA: * Enable phase difference of arrival measurement (RDEV only). - * @MCPS802154_RX_INFO_SP1: + * @MCPS802154_RX_FRAME_CONFIG_SP1: * Enable STS for the received frame, mode 1 (STS after SFD and before PHR, * ERDEV only). - * @MCPS802154_RX_INFO_SP2: + * @MCPS802154_RX_FRAME_CONFIG_SP2: * Enable STS for the received frame, mode 2 (STS after the payload, ERDEV * only). - * @MCPS802154_RX_INFO_SP3: + * @MCPS802154_RX_FRAME_CONFIG_SP3: * Enable STS for the received frame, mode 3 (STS after SFD, no PHR, no * payload, ERDEV only). - * @MCPS802154_RX_INFO_STS_MODE_MASK: + * @MCPS802154_RX_FRAME_CONFIG_STS_MODE_MASK: * Mask covering all the STS mode configuration values. - * @MCPS802154_RX_INFO_RANGING_ROUND: + * @MCPS802154_RX_FRAME_CONFIG_RANGING_ROUND: * Inform low-level driver the expected received frame is the start of a * ranging round (RDEV only). * * If no timestamp flag is given, enable receiver as soon as possible. */ -enum mcps802154_rx_info_flags { - MCPS802154_RX_INFO_TIMESTAMP_DTU = BIT(0), - MCPS802154_RX_INFO_AACK = BIT(1), - MCPS802154_RX_INFO_RANGING = BIT(2), - MCPS802154_RX_INFO_KEEP_RANGING_CLOCK = BIT(3), - MCPS802154_RX_INFO_RANGING_PDOA = BIT(4), - MCPS802154_RX_INFO_SP1 = BIT(5), - MCPS802154_RX_INFO_SP2 = BIT(6), - MCPS802154_RX_INFO_SP3 = BIT(5) | BIT(6), - MCPS802154_RX_INFO_STS_MODE_MASK = BIT(5) | BIT(6), - MCPS802154_RX_INFO_RANGING_ROUND = BIT(7), +enum mcps802154_rx_frame_config_flags { + MCPS802154_RX_FRAME_CONFIG_TIMESTAMP_DTU = BIT(0), + MCPS802154_RX_FRAME_CONFIG_AACK = BIT(1), + MCPS802154_RX_FRAME_CONFIG_RANGING = BIT(2), + MCPS802154_RX_FRAME_CONFIG_KEEP_RANGING_CLOCK = BIT(3), + MCPS802154_RX_FRAME_CONFIG_RANGING_PDOA = BIT(4), + MCPS802154_RX_FRAME_CONFIG_SP1 = BIT(5), + MCPS802154_RX_FRAME_CONFIG_SP2 = BIT(6), + MCPS802154_RX_FRAME_CONFIG_SP3 = BIT(5) | BIT(6), + MCPS802154_RX_FRAME_CONFIG_STS_MODE_MASK = BIT(5) | BIT(6), + MCPS802154_RX_FRAME_CONFIG_RANGING_ROUND = BIT(7), }; /** - * struct mcps802154_rx_info - Information for enabling the receiver. + * struct mcps802154_rx_frame_config - Information for enabling the receiver. */ -struct mcps802154_rx_info { +struct mcps802154_rx_frame_config { /** * @timestamp_dtu: If timestamped, date to enable the receiver. */ @@ -287,7 +412,13 @@ struct mcps802154_rx_info { */ int timeout_dtu; /** - * @flags: See &enum mcps802154_rx_info_flags. + * @frame_timeout_dtu: If no zero, timeout value for the full frame + * reception. This allow limiting the length of accepted frame. The + * timeout starts after &mcps802154_rx_frame_config.timeout_dtu value. + */ + int frame_timeout_dtu; + /** + * @flags: See &enum mcps802154_rx_frame_config_flags. */ u8 flags; /** @@ -363,7 +494,8 @@ struct mcps802154_rx_frame_info { */ int frame_duration_dtu; /** - * @rssi: Received signal strength indication (RSSI). + * @rssi: Received signal strength indication (RSSI), + * absolute value in Q1 fixed point format. */ int rssi; /** @@ -378,16 +510,6 @@ struct mcps802154_rx_frame_info { */ int ranging_offset_rctu; /** - * @ranging_pdoa_rad_q11: Phase difference of arrival, unit is radian - * multiplied by 2048 (RDEV only). - */ - int ranging_pdoa_rad_q11; - /** - * @ranging_aoa_rad_q11: AoA interpolated by the driver from its - * calibration LUT. unit is rad multiplied by 2048 (RDEV only). - */ - int ranging_aoa_rad_q11; - /** * @ranging_sts_timestamp_diffs_rctu: For each SRMARKERx, difference * between the measured timestamp and the expected timestamp relative to * RMARKER in ranging count time unit (ERDEV only). When STS mode is @@ -423,6 +545,190 @@ struct mcps802154_rx_frame_info { }; /** + * enum mcps802154_rx_measurement_info_flags - Flags for measurements on a received + * frame. + * @MCPS802154_RX_MEASUREMENTS_TIMESTAMP: + * Set by MCPS to request time of arrival measurement and associated figure + * of merit (RDEV only). + * @MCPS802154_RX_MEASUREMENTS_CLOCK_OFFSET: + * Set by MCPS to request clock characterization data (RDEV only). + * @MCPS802154_RX_MEASUREMENTS_STS_SEGS_TIMESTAMPS: + * Set by MCPS to request time of arrival measurement on STS segments and + * associated figure of merit (ERDEV only). + * @MCPS802154_RX_MEASUREMENTS_RSSIS: + * Set by MCPS to request RSSI values. + * @MCPS802154_RX_MEASUREMENTS_AOAS: + * Set by MCPS to request angle of arrival measurements, time difference of + * arrival, phase difference of arrival and associated figure of merit + * (RDEV only). + * @MCPS802154_RX_MEASUREMENTS_CIRS: + * Set by MCPS to request CIR samples (RDEV only). + * @MCPS802154_RX_MEASUREMENTS_VENDOR0: + * Set by MCPS to request first set of vendor specific measurements. + * @MCPS802154_RX_MEASUREMENTS_VENDOR1: + * Set by MCPS to request second set of vendor specific measurements. + * @MCPS802154_RX_MEASUREMENTS_VENDOR2: + * Set by MCPS to request third set of vendor specific measurements. + * @MCPS802154_RX_MEASUREMENTS_VENDOR3: + * Set by MCPS to request fourth set of vendor specific measurements. + * + * The low-level driver must clear the corresponding flag if the information is + * not available. + */ +enum mcps802154_rx_measurement_info_flags { + MCPS802154_RX_MEASUREMENTS_TIMESTAMP = BIT(0), + MCPS802154_RX_MEASUREMENTS_CLOCK_OFFSET = BIT(1), + MCPS802154_RX_MEASUREMENTS_STS_SEGS_TIMESTAMPS = BIT(2), + MCPS802154_RX_MEASUREMENTS_RSSIS = BIT(3), + MCPS802154_RX_MEASUREMENTS_AOAS = BIT(4), + MCPS802154_RX_MEASUREMENTS_CIRS = BIT(5), + MCPS802154_RX_MEASUREMENTS_VENDOR0 = BIT(12), + MCPS802154_RX_MEASUREMENTS_VENDOR1 = BIT(13), + MCPS802154_RX_MEASUREMENTS_VENDOR2 = BIT(14), + MCPS802154_RX_MEASUREMENTS_VENDOR3 = BIT(15), +}; + +/** + * struct mcps802154_rx_aoa_measurements - Angle of arrival measurements on a + * received frame (RDEV only). + */ +struct mcps802154_rx_aoa_measurements { + /** + * @tdoa_rctu: Time difference of arrival, in ranging count time unit. + */ + s16 tdoa_rctu; + /** + * @pdoa_rad_q11: Phase difference of arrival, unit is radian multiplied + * by 2048. + */ + s16 pdoa_rad_q11; + /** + * @aoa_rad_q11: Angle of arrival, unit is radian multiplied by 2048. + */ + s16 aoa_rad_q11; + /** + * @fom: Measurements figure of merit (FoM). Range is 0 to 255, with 0 + * being an invalid measure and 255 being a 100% confidence. + */ + u8 fom; + /** + * @type: Measurement type (azimuth, elevation...). Actual value is + * driver dependant. + */ + u8 type; +}; + +/** + * struct mcps802154_rx_cir_sample_window - Window of CIR samples. + */ +struct mcps802154_rx_cir_sample_window { + /** + * @n_samples: The number of samples contained in the window. + */ + u16 n_samples; + /** + * @sizeof_sample: The size of a single sample. + */ + u16 sizeof_sample; + /** + * @samples: CIR samples values. + * + * Each sample is composed of the real and imaginary part which are + * signed numbers. Each sample is encoded using the platform endianness + * with @mcps802154_rx_cir_sample_window.sizeof_sample bytes, first half + * is the real part, second half is the imaginary part. + * + * Must be kept valid until next received frame + */ + void *samples; +}; + +/** + * struct mcps802154_rx_cir - CIR measurements. + */ +struct mcps802154_rx_cir { + /** + * @fp_index: The absolute index of the sample considered as first path. + */ + u16 fp_index; + /** + * @fp_snr: The SNR of the sample considered as first path. + */ + s16 fp_snr; + /** + * @fp_ns_q6: (Q10.6) Time in nanosecond of the first path index + */ + u16 fp_ns_q6; + /** + * @pp_index: The absolute index of the sample considered as peak path. + */ + u16 pp_index; + /** + * @pp_snr: The SNR of the sample considered as peak path. + */ + s16 pp_snr; + /** + * @pp_ns_q6: (Q10.6) Time in nanosecond of the peak path index + */ + u16 pp_ns_q6; + /** + * @fp_sample_offset: The offset of the first path in the sample window. + */ + u16 fp_sample_offset; + /** + * @sample_window: CIR samples. + */ + struct mcps802154_rx_cir_sample_window sample_window; +}; + +/** + * struct mcps802154_rx_measurement_info - Measurements on a received frame. + */ +struct mcps802154_rx_measurement_info { + /** + * @n_rssis: The number of RSSI computed for this frame. Depends on the + * antenna set used to receive. + * + * Set by low-level driver. + */ + int n_rssis; + /** + * @rssis_q1: Received signal strength indication (RSSI), array of + * absolute values in Q7.1 fixed point format, unit is dBm. + */ + u8 rssis_q1[MCPS802154_RSSIS_N_MAX]; + /** + * @n_aoas: Number of angle of arrival measurements. + * + * Set by low-level driver. + */ + int n_aoas; + /** + * @aoas: Angle of arrival measurements, ordered by increasing + * measurement type. + */ + struct mcps802154_rx_aoa_measurements + aoas[MCPS802154_RX_AOA_MEASUREMENTS_MAX]; + /** + * @n_cirs: Number of parts of CIR measurements. + * + * Set by low-level driver. + */ + int n_cirs; + /** + * @cirs: CIR measurements for different parts of the frame. + * + * Set by low-level driver, must be kept valid until next received + * frame. + */ + struct mcps802154_rx_cir *cirs; + /** + * @flags: See &enum mcps802154_rx_measurement_info_flags. + */ + int flags; +}; + +/** * struct mcps802154_sts_params - STS parameters for HRP UWB. */ struct mcps802154_sts_params { @@ -458,6 +764,246 @@ struct mcps802154_sts_params { }; /** + * enum mcps802154_prf - Pulse repetition frequency. + * @MCPS802154_PRF_16: + * 16 MHz, only used in 4a. + * @MCPS802154_PRF_64: + * 64 MHz, used for 4a and 4z BPRF. + * @MCPS802154_PRF_125: + * 125 MHz, used for 4z HPRF. + * @MCPS802154_PRF_250: + * 250 MHz, used for 4z HPRF. + */ +enum mcps802154_prf { + MCPS802154_PRF_16 = 16, + MCPS802154_PRF_64 = 64, + MCPS802154_PRF_125 = 125, + MCPS802154_PRF_250 = 250, +}; + +/** + * enum mcps802154_psr - Number of preamble symbol repetitions in the SYNC + * sequence. + * @MCPS802154_PSR_16: + * 16 symbols, used in 4a and 4z HPRF. + * @MCPS802154_PSR_24: + * 24 symbols, used only in 4z HPRF. + * @MCPS802154_PSR_32: + * 32 symbols, used only in 4z HPRF. + * @MCPS802154_PSR_48: + * 48 symbols, used only in 4z HPRF. + * @MCPS802154_PSR_64: + * 64 symbols, used 4a and 4z BPRF and HPRF. + * @MCPS802154_PSR_96: + * 96 symbols, used only in 4z HPRF. + * @MCPS802154_PSR_128: + * 128 symbols, used only in 4z HPRF. + * @MCPS802154_PSR_256: + * 256 symbols, used only in 4z HPRF. + * @MCPS802154_PSR_1024: + * 1024 symbols, used only in 4a. + * @MCPS802154_PSR_4096: + * 4096 symbols, used only in 4a. + */ +enum mcps802154_psr { + MCPS802154_PSR_16 = 16, + MCPS802154_PSR_24 = 24, + MCPS802154_PSR_32 = 32, + MCPS802154_PSR_48 = 48, + MCPS802154_PSR_64 = 64, + MCPS802154_PSR_96 = 96, + MCPS802154_PSR_128 = 128, + MCPS802154_PSR_256 = 256, + MCPS802154_PSR_1024 = 1024, + MCPS802154_PSR_4096 = 4096, +}; + +/** + * enum mcps802154_sfd - sfd type selector. + * @MCPS802154_SFD_4A: + * SFD defined in 4a, length of 8 symbols. + * @MCPS802154_SFD_4Z_4: + * SFD defined in 4z, length of 4 symbols. + * @MCPS802154_SFD_4Z_8: + * SFD defined in 4z, length of 8 symbols. + * @MCPS802154_SFD_4Z_16: + * SFD defined in 4z, length of 16 symbols. + * @MCPS802154_SFD_4Z_32: + * SFD defined in 4z, length of 32 symbols. + */ +enum mcps802154_sfd { + MCPS802154_SFD_4A, + MCPS802154_SFD_4Z_4, + MCPS802154_SFD_4Z_8, + MCPS802154_SFD_4Z_16, + MCPS802154_SFD_4Z_32, +}; + +/** + * enum mcps802154_data_rate - Data rate. + * @MCPS802154_DATA_RATE_850K: + * 850 kbps, used only for 4a. + * @MCPS802154_DATA_RATE_6M81: + * 6.81 Mbps, used for 4a and 4z (PRF must be 125MHz). + * @MCPS802154_DATA_RATE_7M80: + * 7.80 Mbps, only used for 4z (PRF must be 125MHz). + * @MCPS802154_DATA_RATE_27M2: + * 27.2 Mbps, used for 4a and 4z (PRF must be 250MHz). + * @MCPS802154_DATA_RATE_31M2: + * 31.2 Mbps, used for 4z (PRF must be 250MHz). + * NOTE: device specific values can be set to use a custom data rate. + */ +enum mcps802154_data_rate { + MCPS802154_DATA_RATE_850K = 0, + MCPS802154_DATA_RATE_6M81 = 6, + MCPS802154_DATA_RATE_7M80 = 7, + MCPS802154_DATA_RATE_27M2 = 27, + MCPS802154_DATA_RATE_31M2 = 31, +}; + +/** + * enum mcps802154_hrp_uwb_psdu_size - PSDU size in HPRF. + * @MCPS802154_HRP_UWB_PSDU_SIZE_1023: + * 1023-bytes PSDU. + * @MCPS802154_HRP_UWB_PSDU_SIZE_2047: + * 2047-bytes PSDU. + * @MCPS802154_HRP_UWB_PSDU_SIZE_4095: + * 4095-bytes PSDU. + */ +enum mcps802154_hrp_uwb_psdu_size { + MCPS802154_HRP_UWB_PSDU_SIZE_1023 = 0, + MCPS802154_HRP_UWB_PSDU_SIZE_2047 = 1, + MCPS802154_HRP_UWB_PSDU_SIZE_4095 = 2, +}; + +/** + * struct mcps802154_hrp_uwb_params - Parameters for HRP UWB. + * + * Parameters are given directly to driver without checking. The driver needs to + * check the parameters for supported values, but it can accept non-standard + * values. + */ +struct mcps802154_hrp_uwb_params { + /** + * @prf: Nominal mean Pulse Repetition Frequency. + * + * For 4a, one of MCPS802154_PRF_16 or MCPS802154_PRF_64. + * + * For 4z BPRF, must be MCPS802154_PRF_64. + * + * For 4z HPRF, one of MCPS802154_PRF_125 or MCPS802154_PRF_250. + */ + enum mcps802154_prf prf; + /** + * @psr: Number of preamble symbol repetitions in the SYNC sequence, or + * preamble length. + * + * For 4a, one of 16, 64, 1024 or 4096. + * + * For 4z BPRF, must be 64. + * + * For 4z HPRF, one of 16, 24, 32, 48, 64, 96, 128 or 256. + */ + enum mcps802154_psr psr; + /** + * @sfd_selector: SFD type selector. + * + * When MCPS802154_SFD_4A, use short SFD defined in 802.15.4a. + * + * When MCPS802154_SFD_4Z_*, use SFD defined in 802.15.4z, with length + * 4, 8, 16 or 32. + * + * For 4a, must be MCPS802154_SFD_4A. + * + * For 4z BPRF, one of MCPS802154_SFD_4A or MCPS802154_SFD_4Z_8. + * + * For 4z HPRF, one of MCPS802154_SFD_4Z_{4,8,16,32}. + */ + enum mcps802154_sfd sfd_selector; + /** + * @data_rate: Data rate. + * + * For 4a, one of 850 kbps, 6.81 Mbps or 27.2 Mbps. + * + * For 4z BPRF, must be 6.81 Mbps. + * + * For 4z HPRF at 125 MHz, use 6.81 Mbps or 7.8 Mbps. + * + * For 4z HPRF at 250 MHz, use 27.2 Mbps or 31.2 Mbps. + */ + int data_rate; + /** + * @phr_hi_rate: Use high PHR data rate, for 4z BPRF only. + * + * For 4a and 4z HPRF, this parameter is ignored. + * + * For 4z BPRF, when enabled use 6.81 Mbps, otherwise use 850 kbps. + */ + bool phr_hi_rate; + /** + * @psdu_size: PSDU size in HPRF. + */ + enum mcps802154_hrp_uwb_psdu_size psdu_size; +}; + +/** + * enum mcps802154_antenna_caps - Antenna set capabilities + * @MCPS802154_AOA_X_AXIS: + * Antenna can report azimuth + * @MCPS802154_AOA_Y_AXIS: + * Antenna can report elevation + */ +enum mcps802154_antenna_caps { + MCPS802154_AOA_X_AXIS = BIT(0), + MCPS802154_AOA_Y_AXIS = BIT(1), +}; + +/** + * enum mcps802154_power_state - Power states + * @MCPS802154_PWR_STATE_OFF: + * Power off state. + * @MCPS802154_PWR_STATE_SLEEP: + * Deep sleep state. + * @MCPS802154_PWR_STATE_IDLE: + * Idle state, ready to transmit or receive. + * @MCPS802154_PWR_STATE_RX: + * Receive state. + * @MCPS802154_PWR_STATE_TX: + * Transmit state. + * @MCPS802154_PWR_STATE_MAX: + * Total power states count. + */ +enum mcps802154_power_state { + MCPS802154_PWR_STATE_OFF, + MCPS802154_PWR_STATE_SLEEP, + MCPS802154_PWR_STATE_IDLE, + MCPS802154_PWR_STATE_RX, + MCPS802154_PWR_STATE_TX, + MCPS802154_PWR_STATE_MAX +}; + +/** + * struct mcps802154_power_state_stats - Statistics for a power state. + * @dur: Duration in this power state in ns. + * @count: Count of transitions in this power state. + */ +struct mcps802154_power_state_stats { + u64 dur; + u64 count; +}; + +/** + * struct mcps802154_power_stats - Global power statistics. + * @power_state_stats: Array of power statistics for each power state. + * @interrupts: Hardware interrupts count on the device. + */ +struct mcps802154_power_stats { + struct mcps802154_power_state_stats + power_state_stats[MCPS802154_PWR_STATE_MAX]; + u64 interrupts; +}; + +/** * struct mcps802154_ops - Callback from MCPS to the driver. */ struct mcps802154_ops { @@ -475,7 +1021,7 @@ struct mcps802154_ops { /** * @tx_frame: Transmit a frame. skb contains the buffer starting from * the IEEE 802.15.4 header. The low-level driver should send the frame - * as specified in info. Receiver should be disabled automatically + * as specified in config. Receiver should be disabled automatically * unless a frame is being received. * * The &frame_idx parameter gives the index of the frame in a "block". @@ -489,7 +1035,7 @@ struct mcps802154_ops { * -EBUSY if a reception is happening right now, or any other error. */ int (*tx_frame)(struct mcps802154_llhw *llhw, struct sk_buff *skb, - const struct mcps802154_tx_frame_info *info, + const struct mcps802154_tx_frame_config *config, int frame_idx, int next_delay_dtu); /** * @rx_enable: Enable receiver. @@ -505,8 +1051,8 @@ struct mcps802154_ops { * timestamp, or any other error. */ int (*rx_enable)(struct mcps802154_llhw *llhw, - const struct mcps802154_rx_info *info, int frame_idx, - int next_delay_dtu); + const struct mcps802154_rx_frame_config *config, + int frame_idx, int next_delay_dtu); /** * @rx_disable: Disable receiver, or a programmed receiver enabling, * unless a frame reception is happening right now. @@ -538,6 +1084,14 @@ struct mcps802154_ops { int (*rx_get_error_frame)(struct mcps802154_llhw *llhw, struct mcps802154_rx_frame_info *info); /** + * @rx_get_measurement: Get measurement associated with a received + * frame. + * + * Return: 0, -EBUSY if no longer available, or any other error. + */ + int (*rx_get_measurement)(struct mcps802154_llhw *llhw, void *rx_ctx, + struct mcps802154_rx_measurement_info *info); + /** * @idle: Put the device into idle mode without time limit or until the * given timestamp. The driver should call &mcps802154_timer_expired() * before the given timestamp so that an action can be programmed at the @@ -581,9 +1135,11 @@ struct mcps802154_ops { * * Return: The RMARKER timestamp. */ - u64 (*tx_timestamp_dtu_to_rmarker_rctu)(struct mcps802154_llhw *llhw, - u32 tx_timestamp_dtu, - int ant_set_id); + u64 (*tx_timestamp_dtu_to_rmarker_rctu)( + struct mcps802154_llhw *llhw, u32 tx_timestamp_dtu, + const struct mcps802154_hrp_uwb_params *hrp_uwb_params, + const struct mcps802154_channel *channel_params, + int ant_set_id); /** * @difference_timestamp_rctu: Compute the difference between two * timestamp values. @@ -617,9 +1173,18 @@ struct mcps802154_ops { * * Return: 0 or error. */ - int (*set_hrp_uwb_params)(struct mcps802154_llhw *llhw, int prf, - int psr, int sfd_selector, int phr_rate, - int data_rate); + int (*set_hrp_uwb_params)( + struct mcps802154_llhw *llhw, + const struct mcps802154_hrp_uwb_params *params); + /** + * @check_hrp_uwb_params: Check that the HRP parameters are compatible + * with the hardware capabilities. + * + * Return: 0 or error. + */ + int (*check_hrp_uwb_params)( + struct mcps802154_llhw *llhw, + const struct mcps802154_hrp_uwb_params *params); /** * @set_sts_params: Set STS parameters (ERDEV only). * @@ -706,6 +1271,20 @@ struct mcps802154_ops { */ int (*vendor_cmd)(struct mcps802154_llhw *llhw, u32 vendor_id, u32 subcmd, void *data, size_t data_len); + /** + * @get_antenna_caps: Return antenna set capabilites. + * + * Return: 0 or error. + */ + int (*get_antenna_caps)(struct mcps802154_llhw *llhw, int ant_idx, + u32 *caps); + /** + * @get_power_stats: Get the power statistics. + * + * Return: 0 or error. + */ + int (*get_power_stats)(struct mcps802154_llhw *llhw, + struct mcps802154_power_stats *pwr_stats); #ifdef CONFIG_MCPS802154_TESTMODE /** * @testmode_cmd: Run a testmode command. @@ -736,9 +1315,11 @@ struct mcps802154_ops { * @MCPS802154_RX_ERROR_FILTERED: * A received frame was rejected due to frame filter. * @MCPS802154_RX_ERROR_SFD_TIMEOUT: - * A preamble has been detected but no SFD. + * A preamble has been detected but without SFD. * @MCPS802154_RX_ERROR_OTHER: * Other error, frame reception is aborted. + * @MCPS802154_RX_ERROR_PHR_DECODE: + * the preamble and SFD have been detected but without PHR. * @MCPS802154_RX_ERROR_HPDWARN: * Too late to program RX operation. */ @@ -750,7 +1331,8 @@ enum mcps802154_rx_error_type { MCPS802154_RX_ERROR_FILTERED = 4, MCPS802154_RX_ERROR_SFD_TIMEOUT = 5, MCPS802154_RX_ERROR_OTHER = 6, - MCPS802154_RX_ERROR_HPDWARN = 7, + MCPS802154_RX_ERROR_PHR_DECODE = 7, + MCPS802154_RX_ERROR_HPDWARN = 8, }; /** diff --git a/mac/include/net/mcps802154_frame.h b/mac/include/net/mcps802154_frame.h index 6052f5d..2bc8105 100644 --- a/mac/include/net/mcps802154_frame.h +++ b/mac/include/net/mcps802154_frame.h @@ -25,6 +25,7 @@ #define NET_MCPS802154_FRAME_H #include <linux/skbuff.h> +#include "mcps802154.h" #define IEEE802154_FC_NO_SEQ_SHIFT 8 #define IEEE802154_FC_NO_SEQ (1 << IEEE802154_FC_NO_SEQ_SHIFT) @@ -270,13 +271,16 @@ int mcps802154_get_current_timestamp_dtu(struct mcps802154_llhw *llhw, * device time unit (RDEV only). * @llhw: Low-level device pointer. * @tx_timestamp_dtu: TX timestamp in device time unit. + * @hrp_uwb_params: HRP UWB parameters. + * @channel_params: Channel parameters. * @ant_set_id: Antennas set id used to transmit. * * Return: RMARKER timestamp in ranging count time unit. */ -u64 mcps802154_tx_timestamp_dtu_to_rmarker_rctu(struct mcps802154_llhw *llhw, - u32 tx_timestamp_dtu, - int ant_set_id); +u64 mcps802154_tx_timestamp_dtu_to_rmarker_rctu( + struct mcps802154_llhw *llhw, u32 tx_timestamp_dtu, + const struct mcps802154_hrp_uwb_params *hrp_uwb_params, + const struct mcps802154_channel *channel_params, int ant_set_id); /** * mcps802154_difference_timestamp_rctu() - Compute the difference between two @@ -316,4 +320,15 @@ int mcps802154_compute_frame_duration_dtu(struct mcps802154_llhw *llhw, int mcps802154_vendor_cmd(struct mcps802154_llhw *llhw, u32 vendor_id, u32 subcmd, void *data, size_t data_len); +/** + * mcps802154_rx_get_measurement() - Get measurement. + * @llhw: Low-level device pointer. + * @rx_ctx: Rx context (can be NULL). + * @info: Measurements updated by the llhw. + * + * Return: 0 or error. + */ +int mcps802154_rx_get_measurement(struct mcps802154_llhw *llhw, void *rx_ctx, + struct mcps802154_rx_measurement_info *info); + #endif /* NET_MCPS802154_FRAME_H */ diff --git a/mac/include/net/mcps802154_nl.h b/mac/include/net/mcps802154_nl.h index 8423a9d..9aa5a9b 100644 --- a/mac/include/net/mcps802154_nl.h +++ b/mac/include/net/mcps802154_nl.h @@ -55,12 +55,10 @@ * @MCPS802154_CMD_TESTMODE: * Run a testmode command with TESTDATA blob attribute to pass through * to the driver. - * @MCPS802154_CMD_SET_RANGING_REQUESTS: - * Set the list of ranging requests. - * @MCPS802154_CMD_RANGING_REPORT: - * Result of ranging. - * @MCPS802154_CMD_PING_PONG_REPORT: - * Result of a ping pong request. + * @MCPS802154_CMD_CLOSE_SCHEDULER: + * Close current scheduler and its regions. + * @MCPS802154_CMD_GET_PWR_STATS: + * Get the power statistics. * * @MCPS802154_CMD_UNSPEC: Invalid command. * @__MCPS802154_CMD_AFTER_LAST: Internal use. @@ -71,26 +69,18 @@ enum mcps802154_commands { MCPS802154_CMD_GET_HW, /* can dump */ MCPS802154_CMD_NEW_HW, - MCPS802154_CMD_SET_SCHEDULER, MCPS802154_CMD_SET_SCHEDULER_PARAMS, MCPS802154_CMD_CALL_SCHEDULER, - MCPS802154_CMD_SET_SCHEDULER_REGIONS, MCPS802154_CMD_SET_REGIONS_PARAMS, MCPS802154_CMD_CALL_REGION, - MCPS802154_CMD_SET_CALIBRATIONS, MCPS802154_CMD_GET_CALIBRATIONS, MCPS802154_CMD_LIST_CALIBRATIONS, - MCPS802154_CMD_TESTMODE, - - /* Temporary ranging interface. */ - MCPS802154_CMD_SET_RANGING_REQUESTS, - MCPS802154_CMD_RANGING_REPORT, - MCPS802154_CMD_PING_PONG_REPORT, - + MCPS802154_CMD_CLOSE_SCHEDULER, + MCPS802154_CMD_GET_PWR_STATS, __MCPS802154_CMD_AFTER_LAST, MCPS802154_CMD_MAX = __MCPS802154_CMD_AFTER_LAST - 1 }; @@ -119,13 +109,8 @@ enum mcps802154_commands { * driver-specific attributes. * @MCPS802154_ATTR_CALIBRATIONS: * Nested array of calibrations. - * @MCPS802154_ATTR_RANGING_REQUESTS: - * List of ranging requests. This is a nested attribute containing an array - * of nested attributes. - * @MCPS802154_ATTR_RANGING_RESULT: - * Ranging result, this is a nested attribute. - * @MCPS802154_ATTR_PING_PONG_RESULT: - * Ping pong result, this is a nested attribute. + * @MCPS802154_ATTR_PWR_STATS: + * Nested power statistics data. * * @MCPS802154_ATTR_UNSPEC: Invalid command. * @__MCPS802154_ATTR_AFTER_LAST: Internal use. @@ -150,10 +135,7 @@ enum mcps802154_attrs { MCPS802154_ATTR_CALIBRATIONS, - /* Temporary ranging interface. */ - MCPS802154_ATTR_RANGING_REQUESTS, - MCPS802154_ATTR_RANGING_RESULT, - MCPS802154_ATTR_PING_PONG_RESULT, + MCPS802154_ATTR_PWR_STATS, __MCPS802154_ATTR_AFTER_LAST, MCPS802154_ATTR_MAX = __MCPS802154_ATTR_AFTER_LAST - 1 @@ -191,35 +173,6 @@ enum mcps802154_region_attrs { }; /** - * enum mcps802154_ranging_request_attrs - Ranging request. - * - * @MCPS802154_RANGING_REQUEST_ATTR_ID: - * Request identifier, returned in report. - * @MCPS802154_RANGING_REQUEST_ATTR_FREQUENCY_HZ: - * Ranging frequency in Hz. - * @MCPS802154_RANGING_REQUEST_ATTR_PEER: - * Ranging peer extended address. - * @MCPS802154_RANGING_REQUEST_ATTR_REMOTE_PEER: - * Ranging remote peer extended address. - * - * @MCPS802154_RANGING_REQUEST_ATTR_UNSPEC: Invalid command. - * @__MCPS802154_RANGING_REQUEST_ATTR_AFTER_LAST: Internal use. - * @MCPS802154_RANGING_REQUEST_ATTR_MAX: Internal use. - */ -enum mcps802154_ranging_request_attrs { - MCPS802154_RANGING_REQUEST_ATTR_UNSPEC, - - MCPS802154_RANGING_REQUEST_ATTR_ID, - MCPS802154_RANGING_REQUEST_ATTR_FREQUENCY_HZ, - MCPS802154_RANGING_REQUEST_ATTR_PEER, - MCPS802154_RANGING_REQUEST_ATTR_REMOTE_PEER, - - __MCPS802154_RANGING_REQUEST_ATTR_AFTER_LAST, - MCPS802154_RANGING_REQUEST_ATTR_MAX = - __MCPS802154_RANGING_REQUEST_ATTR_AFTER_LAST - 1 -}; - -/** * enum mcps802154_calibrations_attrs - Calibration item. * * @MCPS802154_CALIBRATIONS_ATTR_KEY: @@ -246,67 +199,56 @@ enum mcps802154_calibrations_attrs { }; /** - * enum mcps802154_ranging_result_attrs - Ranging result. - * - * @MCPS802154_RANGING_RESULT_ATTR_ID: - * Identifier of request. - * @MCPS802154_RANGING_RESULT_ATTR_TOF_RCTU: - * Time of flight in RCTU. - * @MCPS802154_RANGING_RESULT_ATTR_LOCAL_PDOA_RAD_Q11: - * Local Phase Difference Of Arrival, unit is multiple of 2048. - * @MCPS802154_RANGING_RESULT_ATTR_REMOTE_PDOA_RAD_Q11: - * Remote Phase Difference Of Arrival, unit is multiple of 2048. - * @MCPS802154_RANGING_RESULT_ATTR_LOCAL_PDOA_ELEVATION_RAD_Q11: - * Local Phase Difference Of Arrival with second pair antenna, unit is multiple of 2048. - * @MCPS802154_RANGING_RESULT_ATTR_REMOTE_PDOA_ELEVATION_RAD_Q11: - * Remote Phase Difference Of Arrival with second pair antenna, unit is multiple of 2048. - * - * @MCPS802154_RANGING_RESULT_ATTR_UNSPEC: Invalid command. - * @__MCPS802154_RANGING_RESULT_ATTR_AFTER_LAST: Internal use. - * @MCPS802154_RANGING_RESULT_ATTR_MAX: Internal use. + * enum mcps802154_nl_pwr_stats_state_attrs - Power state item. + * + * @MCPS802154_PWR_STATS_STATE_ATTR_TIME: + * Time spent in this state. + * @MCPS802154_PWR_STATS_STATE_ATTR_COUNT: + * Number of transitions to this state. + * @MCPS802154_PWR_STATS_STATE_ATTR_UNSPEC: Invalid command. + * @__MCPS802154_PWR_STATS_STATE_ATTR_AFTER_LAST: Internal use. + * @MCPS802154_PWR_STATS_STATE_ATTR_MAX: Internal use. */ -enum mcps802154_ranging_result_attrs { - MCPS802154_RANGING_RESULT_ATTR_UNSPEC, +enum mcps802154_nl_pwr_stats_state_attrs { + MCPS802154_PWR_STATS_STATE_ATTR_UNSPEC, - MCPS802154_RANGING_RESULT_ATTR_ID, - MCPS802154_RANGING_RESULT_ATTR_TOF_RCTU, - MCPS802154_RANGING_RESULT_ATTR_LOCAL_PDOA_RAD_Q11, - MCPS802154_RANGING_RESULT_ATTR_REMOTE_PDOA_RAD_Q11, - MCPS802154_RANGING_RESULT_ATTR_LOCAL_PDOA_ELEVATION_RAD_Q11, - MCPS802154_RANGING_RESULT_ATTR_REMOTE_PDOA_ELEVATION_RAD_Q11, + MCPS802154_PWR_STATS_STATE_ATTR_TIME, + MCPS802154_PWR_STATS_STATE_ATTR_COUNT, - __MCPS802154_RANGING_RESULT_ATTR_AFTER_LAST, - MCPS802154_RANGING_RESULT_ATTR_MAX = - __MCPS802154_RANGING_RESULT_ATTR_AFTER_LAST - 1 + __MCPS802154_PWR_STATS_STATE_ATTR_AFTER_LAST, + MCPS802154_PWR_STATS_STATE_ATTR_MAX = + __MCPS802154_PWR_STATS_STATE_ATTR_AFTER_LAST - 1 }; /** - * enum mcps802154_ping_pong_result_attrs - Ping pong result. - * - * @MCPS802154_PING_PONG_RESULT_ATTR_ID: - * Identifier of request. - * @MCPS802154_PING_PONG_RESULT_ATTR_T_0: - * t_0 of ping pong - * @MCPS802154_PING_PONG_RESULT_ATTR_T_3: - * t_3 of ping pong. - * @MCPS802154_PING_PONG_RESULT_ATTR_T_4: - * t_4 of ping pong. - * - * @MCPS802154_PING_PONG_RESULT_ATTR_UNSPEC: Invalid command. - * @__MCPS802154_PING_PONG_RESULT_ATTR_AFTER_LAST: Internal use. - * @MCPS802154_PING_PONG_RESULT_ATTR_MAX: Internal use. + * enum mcps802154_pwr_stats_attrs - Power statistics item. + * + * @MCPS802154_PWR_STATS_ATTR_SLEEP: + * Sleep state nested attribute. + * @MCPS802154_PWR_STATS_ATTR_IDLE: + * Idle state nested attribute. + * @MCPS802154_PWR_STATS_ATTR_RX: + * Rx state nested attribute. + * @MCPS802154_PWR_STATS_ATTR_TX: + * Tx state nested attribute. + * @MCPS802154_PWR_STATS_ATTR_INTERRUPTS: + * Interrupts count attribute. + * @MCPS802154_PWR_STATS_ATTR_UNSPEC: Invalid command. + * @__MCPS802154_PWR_STATS_ATTR_AFTER_LAST: Internal use. + * @MCPS802154_PWR_STATS_ATTR_MAX: Internal use. */ -enum mcps802154_ping_pong_result_attrs { - MCPS802154_PING_PONG_RESULT_ATTR_UNSPEC, - - MCPS802154_PING_PONG_RESULT_ATTR_ID, - MCPS802154_PING_PONG_RESULT_ATTR_T_0, - MCPS802154_PING_PONG_RESULT_ATTR_T_3, - MCPS802154_PING_PONG_RESULT_ATTR_T_4, - - __MCPS802154_PING_PONG_RESULT_ATTR_AFTER_LAST, - MCPS802154_PING_PONG_RESULT_ATTR_MAX = - __MCPS802154_PING_PONG_RESULT_ATTR_AFTER_LAST - 1 +enum mcps802154_pwr_stats_attrs { + MCPS802154_PWR_STATS_ATTR_UNSPEC, + + MCPS802154_PWR_STATS_ATTR_SLEEP, + MCPS802154_PWR_STATS_ATTR_IDLE, + MCPS802154_PWR_STATS_ATTR_RX, + MCPS802154_PWR_STATS_ATTR_TX, + MCPS802154_PWR_STATS_ATTR_INTERRUPTS, + + __MCPS802154_PWR_STATS_ATTR_AFTER_LAST, + MCPS802154_PWR_STATS_ATTR_MAX = + __MCPS802154_PWR_STATS_ATTR_AFTER_LAST - 1 }; #endif /* NET_MCPS802154_NL_H */ diff --git a/mac/include/net/mcps802154_schedule.h b/mac/include/net/mcps802154_schedule.h index 5c0416b..485be5b 100644 --- a/mac/include/net/mcps802154_schedule.h +++ b/mac/include/net/mcps802154_schedule.h @@ -44,6 +44,9 @@ struct mcps802154_nl_ranging_request; * @MCPS802154_ACCESS_METHOD_NOTHING: * Nothing to do, wait for end of region, or a schedule change. Internal, * region handlers must return a NULL access if no access is possible. + * @MCPS802154_ACCESS_METHOD_IDLE: + * Nothing to do, wait for end of region, or a schedule change. + * Trust the access duration to not get the current time. * @MCPS802154_ACCESS_METHOD_IMMEDIATE_RX: * RX as soon as possible, without timeout, with auto-ack. * @MCPS802154_ACCESS_METHOD_IMMEDIATE_TX: @@ -55,6 +58,7 @@ struct mcps802154_nl_ranging_request; */ enum mcps802154_access_method { MCPS802154_ACCESS_METHOD_NOTHING, + MCPS802154_ACCESS_METHOD_IDLE, MCPS802154_ACCESS_METHOD_IMMEDIATE_RX, MCPS802154_ACCESS_METHOD_IMMEDIATE_TX, MCPS802154_ACCESS_METHOD_MULTI, @@ -89,18 +93,19 @@ struct mcps802154_access_frame { bool is_tx; union { /** - * @tx_frame_info: Information for transmitting a frame. Should + * @tx_frame_config: Information for transmitting a frame. Should * have rx_enable_after_tx_dtu == 0. */ - struct mcps802154_tx_frame_info tx_frame_info; + struct mcps802154_tx_frame_config tx_frame_config; /** * @rx: Information for receiving a frame. */ struct { /** - * @rx.info: Information for enabling the receiver. + * @rx.frame_config: Information for enabling the + * receiver. */ - struct mcps802154_rx_info info; + struct mcps802154_rx_frame_config frame_config; /** * @rx.frame_info_flags_request: Information to request * when a frame is received, see @@ -208,6 +213,11 @@ struct mcps802154_access { * ieee802154 interface. */ const struct mcps802154_channel *channel; + /** + * @hrp_uwb_params: If not NULL, parameters for a HRP UWB Phy set at the + * start of a multiple frames access. + */ + const struct mcps802154_hrp_uwb_params *hrp_uwb_params; }; /** @@ -252,6 +262,10 @@ struct mcps802154_access_ops { /** * @tx_get_frame: Return a frame to send, the buffer is lend to caller * and should be returned with &mcps802154_access_ops.tx_return(). + * + * The return value can be NULL for frames without data. In this case, + * &mcps802154_access_ops.tx_return() will be called anyway, with a NULL + * pointer. */ struct sk_buff *(*tx_get_frame)(struct mcps802154_access *access, int frame_idx); @@ -409,6 +423,11 @@ struct mcps802154_region_ops { * Return 1 if the region accepted to transmit the buffer, 0 otherwise. */ int (*xmit_skb)(struct mcps802154_region *region, struct sk_buff *skb); + /** + * @deferred: Called at the end of event processing on request. See + * mcps802154_region_deferred. + */ + void (*deferred)(struct mcps802154_region *region); }; /** @@ -511,6 +530,15 @@ struct mcps802154_scheduler_ops { struct mcps802154_scheduler *scheduler, const struct mcps802154_nl_ranging_request *requests, unsigned int n_requests); + /** + * @get_next_demands: Called to get an aggregated demand for the specified + * region. + */ + int (*get_next_demands)(struct mcps802154_scheduler *scheduler, + const struct mcps802154_region *region, + u32 timestamp_dtu, int duration_dtu, + int delta_dtu, + struct mcps802154_region_demand *demands); }; /** @@ -693,6 +721,22 @@ void mcps802154_region_rx_skb(struct mcps802154_llhw *llhw, struct sk_buff *skb, u8 lqi); /** + * mcps802154_region_deferred() - Request to call the deferred callback at the + * end of event processing. + * @llhw: Low-level device pointer. + * @region: Pointer to the open region. + * + * Event is coming from the low-level device. The region must be the one which + * triggered the event (region must not call this after a get_access). If this + * is not respected, this call can return -EINVAL in case two regions request + * the deferred callback at the same time. + * + * Return: 0 or -EINVAL. + */ +int mcps802154_region_deferred(struct mcps802154_llhw *llhw, + struct mcps802154_region *region); + +/** * mcps802154_scheduler_register() - Register a scheduler, to be called when * your module is loaded. * @scheduler_ops: Scheduler to register. @@ -743,12 +787,14 @@ int mcps802154_schedule_recycle( * @region: Region to add. * @start_dtu: Region start from the start of the schedule. * @duration_dtu: Region duration, or 0 for endless region. + * @once: Schedule the region once, ignoring the remaining region duration. * * Return: 0 or error. */ int mcps802154_schedule_add_region( const struct mcps802154_schedule_update *schedule_update, - struct mcps802154_region *region, int start_dtu, int duration_dtu); + struct mcps802154_region *region, int start_dtu, int duration_dtu, + bool once); /** * mcps802154_reschedule() - Request to change access as possible. @@ -789,4 +835,21 @@ void mcps802154_schedule_invalidate(struct mcps802154_llhw *llhw); int mcps802154_schedule_get_regions(struct mcps802154_llhw *llhw, struct list_head **regions); +/** + * mcps802154_schedule_get_next_demands() - Get an aggregated demand for the + * specified region. + * @llhw: Low-level device pointer. + * @region: Region. + * @timestamp_dtu: Timestamp from which demands must be computed. + * @duration_dtu: Duration for which demands are considered. + * @delta_dtu: Maximum gap between two demands. + * @demands: Aggregated demand. + * + * Return: 1 if demand is returned, 0 if no demand or error. + */ +int mcps802154_schedule_get_next_demands( + struct mcps802154_llhw *llhw, const struct mcps802154_region *region, + u32 timestamp_dtu, int duration_dtu, int delta_dtu, + struct mcps802154_region_demand *demands); + #endif /* NET_MCPS802154_SCHEDULE_H */ diff --git a/mac/include/net/mcps_skb_frag.h b/mac/include/net/mcps_skb_frag.h new file mode 100644 index 0000000..78418f0 --- /dev/null +++ b/mac/include/net/mcps_skb_frag.h @@ -0,0 +1,34 @@ +/* + * This file is part of the UWB stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see <http://www.gnu.org/licenses/>. + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. Please contact Qorvo to inquire about licensing terms. + */ + +#include <linux/skbuff.h> + +/** + * mcps_skb_frags_len() - Return the total length of the fragments attached to this buffer. + * @skb: Pointer to buffer. + * + * Return: Attached fragments length. + * + * NOTE: The parent length is NOT included in the computed value + */ +int mcps_skb_frags_len(struct sk_buff *skb); diff --git a/mac/include/net/nfcc_coex_region_nl.h b/mac/include/net/nfcc_coex_region_nl.h index 2082e48..169691a 100644 --- a/mac/include/net/nfcc_coex_region_nl.h +++ b/mac/include/net/nfcc_coex_region_nl.h @@ -73,7 +73,9 @@ enum nfcc_coex_call_attrs { * @NFCC_COEX_CCC_SESSION_PARAM_ATTR_TIME0_NS: * Initiation time in unit of ns, default 0. * @NFCC_COEX_CCC_SESSION_PARAM_ATTR_CHANNEL_NUMBER: - * Override channel for this session: 5, 6, 8, 9, 10, 12, 13 or 14 + * Override channel for this session: 5, 6, 8, 9, 10, 12, 13 or 14. + * @NFCC_COEX_CCC_SESSION_PARAM_ATTR_VERSION: + * Protocol version to be used. * * @NFCC_COEX_CCC_SESSION_PARAM_ATTR_UNSPEC: Invalid command. * @__NFCC_COEX_CCC_SESSION_PARAM_ATTR_AFTER_LAST: Internal use. @@ -84,6 +86,7 @@ enum nfcc_coex_ccc_session_param_attrs { NFCC_COEX_CCC_SESSION_PARAM_ATTR_TIME0_NS, NFCC_COEX_CCC_SESSION_PARAM_ATTR_CHANNEL_NUMBER, + NFCC_COEX_CCC_SESSION_PARAM_ATTR_VERSION, __NFCC_COEX_CCC_SESSION_PARAM_ATTR_AFTER_LAST, NFCC_COEX_CCC_SESSION_PARAM_ATTR_MAX = diff --git a/mac/include/net/pctt_region_nl.h b/mac/include/net/pctt_region_nl.h index 1402252..f9a674a 100644 --- a/mac/include/net/pctt_region_nl.h +++ b/mac/include/net/pctt_region_nl.h @@ -26,12 +26,7 @@ /** * enum pctt_call - PCTT calls identifiers. - * FIXME: Must be rework, 3 netlink requests to set parameters is too complex. * - * @PCTT_CALL_SET_PARAMS: - * First set parameters. - * TODO: Move all in "start test" parameters (like NFCC_COEX) or, - * SET_PARAMS call_id (like FiRa). * @PCTT_CALL_SESSION_INIT: * Initialize PCTT session. * @PCTT_CALL_SESSION_CMD: @@ -50,7 +45,6 @@ * @PCTT_CALL_MAX: Internal use. */ enum pctt_call { - PCTT_CALL_SET_PARAMS, PCTT_CALL_SESSION_INIT, PCTT_CALL_SESSION_CMD, PCTT_CALL_SESSION_DEINIT, @@ -63,9 +57,7 @@ enum pctt_call { enum pctt_call_attrs { PCTT_CALL_ATTR_UNSPEC, - PCTT_CALL_ATTR_PARAMS, PCTT_CALL_ATTR_CMD_ID, - PCTT_CALL_ATTR_CMD_PARAMS, PCTT_CALL_ATTR_RESULT_DATA, PCTT_CALL_ATTR_SESSION_ID, PCTT_CALL_ATTR_SESSION_STATE, @@ -108,13 +100,41 @@ enum pctt_call_attrs { * @PCTT_SESSION_PARAM_ATTR_PSDU_DATA_RATE: * 6.81 Mbps (0, default), 7.80 Mbps (1, not supported), * 27.2 Mbps (2, not supported), 31.2 Mbps (3, not supported) + * @PCTT_SESSION_PARAM_ATTR_BPRF_PHR_DATA_RATE: + * 850 kbps (0, default) or 6.81 Mbps (1) * @PCTT_SESSION_PARAM_ATTR_MAC_FCS_TYPE: * CRC16 (0, default) or CRC32 (1, not supported) * @PCTT_SESSION_PARAM_ATTR_TX_ADAPTIVE_PAYLOAD_POWER: * Disable adaptive payload power for TX (0, default) or enable (1) * @PCTT_SESSION_PARAM_ATTR_STS_INDEX: * STS index initialization value - * + * @PCTT_SESSION_PARAM_ATTR_STS_LENGTH: + * Number of symbols in a STS segment. 32 (0x00), 64 (0x01, default) or 128 + * symbols (0x02) + * @PCTT_SESSION_PARAM_ATTR_NUM_PACKETS: + * Number of packets (default 1000). + * @PCTT_SESSION_PARAM_ATTR_T_GAP: + * Gap between start of one packet to the next in µs (default 2000). + * @PCTT_SESSION_PARAM_ATTR_T_START: + * Max. time from the start of T_GAP to SFD found state in µs (default + * 450us). + * @PCTT_SESSION_PARAM_ATTR_T_WIN: + * Max. time for which RX is looking for a packet from the start of T_GAP + * in µs (default 750). + * @PCTT_SESSION_PARAM_ATTR_RANDOMIZE_PSDU: + * Disable (0, default) or enable (1) PSDU randomization. + * @PCTT_SESSION_PARAM_ATTR_PHR_RANGING_BIT: + * Disable (0, default) or enable (1) ranging bit field of PHR in both BPRF + * and HPRF. + * @PCTT_SESSION_PARAM_ATTR_RMARKER_TX_START: + * Start time of TX in 1/(128*499.2MHz) units. + * @PCTT_SESSION_PARAM_ATTR_RMARKER_RX_START: + * Start time of RX in 1/(128*499.2MHz) units. + * @PCTT_SESSION_PARAM_ATTR_STS_INDEX_AUTO_INCR: + * Disable (0, default) or enable (1) incrementation of STS_INDEX config + * value for every frame in PER Rx/Periodic TX test. + * @PCTT_SESSION_PARAM_ATTR_DATA_PAYLOAD: + * PSDU Data. * @PCTT_SESSION_PARAM_ATTR_UNSPEC: Invalid command. * @__PCTT_SESSION_PARAM_ATTR_AFTER_LAST: Internal use. * @PCTT_SESSION_PARAM_ATTR_MAX: Internal use. @@ -138,32 +158,29 @@ enum pctt_session_param_attrs { PCTT_SESSION_PARAM_ATTR_SFD_ID, PCTT_SESSION_PARAM_ATTR_NUMBER_OF_STS_SEGMENTS, PCTT_SESSION_PARAM_ATTR_PSDU_DATA_RATE, + PCTT_SESSION_PARAM_ATTR_BPRF_PHR_DATA_RATE, PCTT_SESSION_PARAM_ATTR_MAC_FCS_TYPE, PCTT_SESSION_PARAM_ATTR_TX_ADAPTIVE_PAYLOAD_POWER, /* STS and crypto */ PCTT_SESSION_PARAM_ATTR_STS_INDEX, + PCTT_SESSION_PARAM_ATTR_STS_LENGTH, + /* Test configuration parameters */ + PCTT_SESSION_PARAM_ATTR_NUM_PACKETS, + PCTT_SESSION_PARAM_ATTR_T_GAP, + PCTT_SESSION_PARAM_ATTR_T_START, + PCTT_SESSION_PARAM_ATTR_T_WIN, + PCTT_SESSION_PARAM_ATTR_RANDOMIZE_PSDU, + PCTT_SESSION_PARAM_ATTR_PHR_RANGING_BIT, + PCTT_SESSION_PARAM_ATTR_RMARKER_TX_START, + PCTT_SESSION_PARAM_ATTR_RMARKER_RX_START, + PCTT_SESSION_PARAM_ATTR_STS_INDEX_AUTO_INCR, + /* Payload */ + PCTT_SESSION_PARAM_ATTR_DATA_PAYLOAD, __PCTT_SESSION_PARAM_ATTR_AFTER_LAST, PCTT_SESSION_PARAM_ATTR_MAX = __PCTT_SESSION_PARAM_ATTR_AFTER_LAST - 1 }; -enum pctt_param_attrs { - PCTT_PARAM_ATTR_UNSPEC, - - PCTT_PARAM_ATTR_NUM_PACKETS, - PCTT_PARAM_ATTR_T_GAP, - PCTT_PARAM_ATTR_T_START, - PCTT_PARAM_ATTR_T_WIN, - PCTT_PARAM_ATTR_RANDOMIZE_PSDU, - PCTT_PARAM_ATTR_PHR_RANGING_BIT, - PCTT_PARAM_ATTR_RMARKER_TX_START, - PCTT_PARAM_ATTR_RMARKER_RX_START, - PCTT_PARAM_ATTR_STS_INDEX_AUTO_INCR, - - __PCTT_PARAM_ATTR_AFTER_LAST, - PCTT_PARAM_ATTR_MAX = __PCTT_PARAM_ATTR_AFTER_LAST - 1 -}; - enum pctt_id_attrs { PCTT_ID_ATTR_UNSPEC, @@ -178,15 +195,6 @@ enum pctt_id_attrs { PCTT_ID_ATTR_MAX = __PCTT_ID_ATTR_AFTER_LAST - 1 }; -enum pctt_test_param_attrs { - PCTT_TEST_PARAM_ATTR_UNSPEC, - - PCTT_TEST_PARAM_ATTR_PAYLOAD, - - __PCTT_TEST_PARAM_ATTR_AFTER_LAST, - PCTT_TEST_PARAM_ATTR_MAX = __PCTT_TEST_PARAM_ATTR_AFTER_LAST - 1, -}; - enum pctt_result_data_attrs { PCTT_RESULT_DATA_ATTR_UNSPEC, @@ -213,7 +221,6 @@ enum pctt_result_data_attrs { PCTT_RESULT_DATA_ATTR_PHR, PCTT_RESULT_DATA_ATTR_PSDU_DATA_LEN, PCTT_RESULT_DATA_ATTR_PSDU_DATA, - PCTT_RESULT_DATA_ATTR_TX_TS_INT, PCTT_RESULT_DATA_ATTR_TX_TS_FRAC, PCTT_RESULT_DATA_ATTR_RX_TS_INT, @@ -221,8 +228,14 @@ enum pctt_result_data_attrs { PCTT_RESULT_DATA_ATTR_MEASUREMENT, + PCTT_RESULT_DATA_ATTR_PDOA_AZIMUTH_DEG_Q7, + PCTT_RESULT_DATA_ATTR_PDOA_ELEVATION_DEG_Q7, + PCTT_RESULT_DATA_ATTR_RSSI, + PCTT_RESULT_DATA_ATTR_AOA_AZIMUTH_DEG_Q7, + PCTT_RESULT_DATA_ATTR_AOA_ELEVATION_DEG_Q7, + __PCTT_RESULT_DATA_ATTR_AFTER_LAST, - PCTT_RESULT_DATA_ATTR_MAX = __PCTT_RESULT_DATA_ATTR_AFTER_LAST - 1 + PCTT_RESULT_DATA_ATTR_MAX = __PCTT_RESULT_DATA_ATTR_AFTER_LAST - 1, }; #endif /* NET_PCTT_REGION_NL_H */ diff --git a/mac/include/net/pctt_region_params.h b/mac/include/net/pctt_region_params.h index 4d51060..0a0f745 100644 --- a/mac/include/net/pctt_region_params.h +++ b/mac/include/net/pctt_region_params.h @@ -41,11 +41,30 @@ */ #define PCTT_DATA_PAYLOAD_SIZE_MAX 84 +/** + * enum pctt_device_role - **[NOT IMPLEMENTED]** Role played by a device. + * @PCTT_DEVICE_ROLE_RESPONDER: The device acts as a responder. + * @PCTT_DEVICE_ROLE_INITIATOR: The device acts as an initiator. + * + * Current implementation does not support decorrelation between the + * device's role and the device's type. The controller is always + * the initiator and the controlee is always the responder. + * + * This enum is not used in the current implementation. + */ enum pctt_device_role { PCTT_DEVICE_ROLE_RESPONDER, PCTT_DEVICE_ROLE_INITIATOR, }; +/** + * enum pctt_rframe_config - Rframe configuration used to transmit/receive + * ranging messages. + * @PCTT_RFRAME_CONFIG_SP0: Use SP0 mode. + * @PCTT_RFRAME_CONFIG_SP1: Use SP1 mode. + * @PCTT_RFRAME_CONFIG_SP2: RFU + * @PCTT_RFRAME_CONFIG_SP3: Use SP3 mode. + */ enum pctt_rframe_config { PCTT_RFRAME_CONFIG_SP0, PCTT_RFRAME_CONFIG_SP1, @@ -53,16 +72,42 @@ enum pctt_rframe_config { PCTT_RFRAME_CONFIG_SP3, }; +/** + * enum pctt_prf_mode - Pulse Repetition Frequency mode. + * @PCTT_PRF_MODE_BPRF: Base Pulse Repetition Frequency. + * @PCTT_PRF_MODE_HPRF: Higher Pulse Repetition Frequency. + * @PCTT_PRF_MODE_HPRF_HIGH_RATE: Higher Pulse Repetition Frequency allowing + * higher data rates (27M2 and 31M2). + * + * This enum is not used in the current implementation. + */ enum pctt_prf_mode { PCTT_PRF_MODE_BPRF, PCTT_PRF_MODE_HPRF, + PCTT_PRF_MODE_HPRF_HIGH_RATE, }; +/** + * enum pctt_preamble_duration - Duration of preamble in symbols. + * @PCTT_PREAMBLE_DURATION_32: 32 symbols duration. + * @PCTT_PREAMBLE_DURATION_64: 64 symbols duration. + */ enum pctt_preamble_duration { PCTT_PREAMBLE_DURATION_32, PCTT_PREAMBLE_DURATION_64, }; +/** + * enum pctt_sfd_id - Start-of-frame delimiter. + * @PCTT_SFD_ID_0: Delimiter is [0 +1 0 –1 +1 0 0 –1] + * @PCTT_SFD_ID_1: Delimiter is [ –1 –1 +1 –1 ] + * @PCTT_SFD_ID_2: Delimiter is [ –1 –1 –1 +1 –1 –1 +1 –1 ] + * @PCTT_SFD_ID_3: Delimiter is + * [ –1 –1 –1 –1 –1 +1 +1 –1 –1 +1 –1 +1 –1 –1 +1 –1 ] + * @PCTT_SFD_ID_4: Delimiter is + * [ –1 –1 –1 –1 –1 –1 –1 +1 –1 –1 +1 –1 –1 +1 –1 +1 –1 +1 + * –1 –1 –1 +1 +1 –1 –1 –1 +1 –1 +1 +1 –1 –1 ] + */ enum pctt_sfd_id { PCTT_SFD_ID_0, PCTT_SFD_ID_1, @@ -71,12 +116,29 @@ enum pctt_sfd_id { PCTT_SFD_ID_4, }; +/** + * enum pctt_number_of_sts_segments - Number of STS segments. + * @PCTT_NUMBER_OF_STS_SEGMENTS_NONE: No STS Segment (Rframe config SP0). + * @PCTT_NUMBER_OF_STS_SEGMENTS_1_SEGMENT: 1 STS Segment. + * @PCTT_NUMBER_OF_STS_SEGMENTS_2_SEGMENTS: 2 STS Segments. + * @PCTT_NUMBER_OF_STS_SEGMENTS_3_SEGMENTS: 3 STS Segments. + * @PCTT_NUMBER_OF_STS_SEGMENTS_4_SEGMENTS: 4 STS Segments. + */ enum pctt_number_of_sts_segments { PCTT_NUMBER_OF_STS_SEGMENTS_NONE, PCTT_NUMBER_OF_STS_SEGMENTS_1_SEGMENT, PCTT_NUMBER_OF_STS_SEGMENTS_2_SEGMENTS, + PCTT_NUMBER_OF_STS_SEGMENTS_3_SEGMENTS, + PCTT_NUMBER_OF_STS_SEGMENTS_4_SEGMENTS, }; +/** + * enum pctt_psdu_data_rate - Data rate used to exchange PSDUs. + * @PCTT_PSDU_DATA_RATE_6M81: 6.8Mb/s rate. + * @PCTT_PSDU_DATA_RATE_7M80: 7.8Mb/s rate. + * @PCTT_PSDU_DATA_RATE_27M2: 27.2Mb/s rate. + * @PCTT_PSDU_DATA_RATE_31M2: 31.2Mb/s rate. + */ enum pctt_psdu_data_rate { PCTT_PSDU_DATA_RATE_6M81, PCTT_PSDU_DATA_RATE_7M80, @@ -84,6 +146,18 @@ enum pctt_psdu_data_rate { PCTT_PSDU_DATA_RATE_31M2, }; +/** + * enum pctt_phr_data_rate - Data rate used to exchange PHR. + * @PCTT_PHR_DATA_RATE_850K: 850kb/s rate. + * @PCTT_PHR_DATA_RATE_6M81: 6.8Mb/s rate. + * + * This enum is not used in the current implementation. + */ +enum pctt_phr_data_rate { + PCTT_PHR_DATA_RATE_850K, + PCTT_PHR_DATA_RATE_6M81, +}; + enum pctt_mac_fcs_type { PCTT_MAC_FCS_TYPE_CRC_16, PCTT_MAC_FCS_TYPE_CRC_32, @@ -131,4 +205,16 @@ enum pctt_session_state { PCTT_SESSION_STATE_IDLE, }; +/** + * enum pctt_sts_length - Number of symbols in a STS segment. + * @PCTT_STS_LENGTH_32: The STS length is 32 symbols. + * @PCTT_STS_LENGTH_64: The STS length is 64 symbols. + * @PCTT_STS_LENGTH_128: The STS length is 128 symbols. + */ +enum pctt_sts_length { + PCTT_STS_LENGTH_32 = 0, + PCTT_STS_LENGTH_64 = 1, + PCTT_STS_LENGTH_128 = 2, +}; + #endif /* NET_PCTT_REGION_PARAMS_H */ diff --git a/mac/include/net/simple_ranging_region_nl.h b/mac/include/net/simple_ranging_region_nl.h deleted file mode 100644 index 7dc62ee..0000000 --- a/mac/include/net/simple_ranging_region_nl.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This file is part of the UWB stack for linux. - * - * Copyright (c) 2020-2021 Qorvo US, Inc. - * - * This software is provided under the GNU General Public License, version 2 - * (GPLv2), as well as under a Qorvo commercial license. - * - * You may choose to use this software under the terms of the GPLv2 License, - * version 2 ("GPLv2"), as published by the Free Software Foundation. - * You should have received a copy of the GPLv2 along with this program. If - * not, see <http://www.gnu.org/licenses/>. - * - * This program is distributed under the GPLv2 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 GPLv2 for more - * details. - * - * If you cannot meet the requirements of the GPLv2, you may not use this - * software for any purpose without first obtaining a commercial license from - * Qorvo. Please contact Qorvo to inquire about licensing terms. - */ - -#ifndef SIMPLE_RANGING_REGION_NL_H -#define SIMPLE_RANGING_REGION_NL_H - -/** - * enum simple_ranging_region_set_parameters_attrs - Simple ranging params. - * - * @SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_SLOT_DURATION_MS: - * Slot duration in milliseconds. - * @SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_NODE_TYPE: - * The node type, either 0 for initiator, or 1 for responder. - * @SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_TX_ANTENNA: - * The antenna index for transmit. - * @SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_RX_ANTENNA_PAIR_AZIMUTH: - * The antenna pair index for receive with azimuth AoA. - * @SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_RX_ANTENNA_PAIR_ELEVATION: - * The antenna pair index for receive with elevation AoA. - * - * @SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_UNSPEC: Invalid command. - * @__SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_AFTER_LAST: Internal use. - * @SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_MAX: Internal use. - */ -enum simple_ranging_region_set_parameters_attrs { - SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_UNSPEC, - - SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_SLOT_DURATION_MS, - SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_NODE_TYPE, - SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_TX_ANTENNA, - SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_RX_ANTENNA_PAIR_AZIMUTH, - SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_RX_ANTENNA_PAIR_ELEVATION, - - __SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_AFTER_LAST, - SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_MAX = - __SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_AFTER_LAST - 1 -}; - -#endif /* SIMPLE_RANGING_REGION_NL_H */ diff --git a/mac/include/net/vendor_cmd.h b/mac/include/net/vendor_cmd.h index bb64452..38a6224 100644 --- a/mac/include/net/vendor_cmd.h +++ b/mac/include/net/vendor_cmd.h @@ -25,30 +25,40 @@ #define NET_VENDOR_CMD_H #include <linux/types.h> +#include <net/mcps802154.h> /** - * enum dw3000_vendor_cmd - Vendor command identifiers. - * @DW3000_VENDOR_CMD_NFCC_COEX_HANDLE_ACCESS: - * NFCC Coex: handle access. - * @DW3000_VENDOR_CMD_NFCC_COEX_GET_ACCESS_INFORMATION: - * NFCC Coex: get access information. - * @DW3000_VENDOR_CMD_NFCC_COEX_STOP: - * NFCC Coex: stop. - * @DW3000_VENDOR_CMD_PCTT_SETUP_HW: + * enum llhw_vendor_cmd - Vendor command identifiers. + * @LLHW_VENDOR_CMD_NFCC_COEX_HANDLE_ACCESS: + * NFCC Coex: Handle access. + * @LLHW_VENDOR_CMD_NFCC_COEX_GET_ACCESS_INFORMATION: + * NFCC Coex: Get access information. + * @LLHW_VENDOR_CMD_NFCC_COEX_STOP: + * NFCC Coex: Stop. + * @LLHW_VENDOR_CMD_PCTT_SETUP_HW: * PCTT: Setup hardware access. + * @LLHW_VENDOR_CMD_PCTT_HANDLE_LOOPBACK: + * PCTT: Handle loop-back test. + * @LLHW_VENDOR_CMD_PCTT_GET_LOOPBACK_INFO: + * PCTT: Get loop-back information. + * @LLHW_VENDOR_CMD_PCTT_GET_FRAME_INFO: + * PCTT: Get the last received frame information. */ -enum dw3000_vendor_cmd { - DW3000_VENDOR_CMD_NFCC_COEX_HANDLE_ACCESS, - DW3000_VENDOR_CMD_NFCC_COEX_GET_ACCESS_INFORMATION, - DW3000_VENDOR_CMD_NFCC_COEX_STOP, - DW3000_VENDOR_CMD_PCTT_SETUP_HW, +enum llhw_vendor_cmd { + LLHW_VENDOR_CMD_NFCC_COEX_HANDLE_ACCESS, + LLHW_VENDOR_CMD_NFCC_COEX_GET_ACCESS_INFORMATION, + LLHW_VENDOR_CMD_NFCC_COEX_STOP, + LLHW_VENDOR_CMD_PCTT_SETUP_HW, + LLHW_VENDOR_CMD_PCTT_HANDLE_LOOPBACK, + LLHW_VENDOR_CMD_PCTT_GET_LOOPBACK_INFO, + LLHW_VENDOR_CMD_PCTT_GET_FRAME_INFO, }; /** - * struct dw3000_vendor_cmd_nfcc_coex_handle_access - NFCC Coex: handle access + * struct llhw_vendor_cmd_nfcc_coex_handle_access - NFCC Coex: handle access * vendor command. */ -struct dw3000_vendor_cmd_nfcc_coex_handle_access { +struct llhw_vendor_cmd_nfcc_coex_handle_access { /** * @start: True to start a new session. */ @@ -69,13 +79,17 @@ struct dw3000_vendor_cmd_nfcc_coex_handle_access { * @chan: Channel number, 5 or 9. */ int chan; + /** + * @version: Protocol version. + */ + int version; }; /** - * struct dw3000_vendor_cmd_nfcc_coex_get_access_info - NFCC Coex: get access + * struct llhw_vendor_cmd_nfcc_coex_get_access_info - NFCC Coex: get access * info vendor command. */ -struct dw3000_vendor_cmd_nfcc_coex_get_access_info { +struct llhw_vendor_cmd_nfcc_coex_get_access_info { /** * @stop: If true, the NFCC did not give a next access. */ @@ -102,10 +116,31 @@ struct dw3000_vendor_cmd_nfcc_coex_get_access_info { }; /** - * struct dw3000_vendor_cmd_pctt_setup_hw - PCTT: direct HW access + * struct llhw_vendor_cmd_nfcc_coex_stop - NFCC Coex: stop * vendor command. */ -struct dw3000_vendor_cmd_pctt_setup_hw { +struct llhw_vendor_cmd_nfcc_coex_stop { + /** + * @timestamp_dtu: + * Access date when the stop must be sent. + */ + u32 timestamp_dtu; + /** + * @duration_dtu: + * Duration of the access. + */ + int duration_dtu; + /** + * @version: Protocol version. + */ + int version; +}; + +/** + * struct llhw_vendor_cmd_pctt_setup_hw - PCTT: direct HW access + * vendor command. + */ +struct llhw_vendor_cmd_pctt_setup_hw { /** * @chan: Channel number, 5 or 9. */ @@ -133,4 +168,76 @@ struct dw3000_vendor_cmd_pctt_setup_hw { u8 preamble_duration; }; +/** + * struct llhw_vendor_cmd_pctt_handle_loopback - PCTT: handle loopback access. + */ +struct llhw_vendor_cmd_pctt_handle_loopback { + /** + * @ant_set_id : antenna set index to use for transmit/receive. + */ + int ant_set_id; + /** + * @rx_timeout_dtu: If negative, no timeout, if zero, use a default timeout + * value, else this is the timeout value in device time unit. + */ + int rx_timeout_dtu; + /** + * @rx_frame_timeout_dtu: If no zero, timeout value for the full frame + * reception. This allow limiting the length of accepted frame. The + * timeout starts after rx_timeout_dtu value. + */ + int rx_frame_timeout_dtu; + /** + * @data_payload: Array of payload to send during loopback test. + */ + const u8 *data_payload; + /** + * @data_payload_len: Length of the payload array in byte. + */ + size_t data_payload_len; +}; + +/** + * struct llhw_vendor_cmd_pctt_get_loopback_info - PCTT: get access + * info vendor command. + */ +struct llhw_vendor_cmd_pctt_get_loopback_info { + /** + * @skb: sk buffer containing received data. + */ + struct sk_buff *skb; + /** + * @success: True when data sent match with received. + */ + bool success; + /** + * @rssi: Received signal strength indication (RSSI), + * absolute value in Q1 fixed point format. + */ + int rssi; + /** + * @rx_timestamp_rctu: RX timestamp in RCTU units. + */ + u64 rx_timestamp_rctu; + /** + * @tx_timestamp_rctu: TX timestamp in RCTU units. + */ + u64 tx_timestamp_rctu; +}; + +/** + * struct llhw_vendor_cmd_pctt_get_frame_info - PCTT: last received frame + * information. + */ +struct llhw_vendor_cmd_pctt_get_frame_info { + /** + * @skb: sk buffer containing received data. + */ + struct sk_buff *skb; + /** + * @info: frame information. + */ + struct mcps802154_rx_frame_info info; +}; + #endif /* NET_VENDOR_CMD_H */ diff --git a/mac/llhw-ops.h b/mac/llhw-ops.h index 69aa986..1dfcf54 100644 --- a/mac/llhw-ops.h +++ b/mac/llhw-ops.h @@ -48,20 +48,20 @@ static inline void llhw_stop(struct mcps802154_local *local) static inline int llhw_tx_frame(struct mcps802154_local *local, struct sk_buff *skb, - const struct mcps802154_tx_frame_info *info, + const struct mcps802154_tx_frame_config *config, int frame_idx, int next_delay_dtu) { int r; - trace_llhw_tx_frame(local, info, frame_idx, next_delay_dtu); - r = local->ops->tx_frame(&local->llhw, skb, info, frame_idx, + trace_llhw_tx_frame(local, config, frame_idx, next_delay_dtu); + r = local->ops->tx_frame(&local->llhw, skb, config, frame_idx, next_delay_dtu); trace_llhw_return_int(local, r); return r; } static inline int llhw_rx_enable(struct mcps802154_local *local, - const struct mcps802154_rx_info *info, + const struct mcps802154_rx_frame_config *info, int frame_idx, int next_delay_dtu) { int r; @@ -141,12 +141,14 @@ static inline int llhw_get_current_timestamp_dtu(struct mcps802154_local *local, return r; } -static inline u64 -llhw_tx_timestamp_dtu_to_rmarker_rctu(struct mcps802154_local *local, - u32 tx_timestamp_dtu, int ant_set_id) +static inline u64 llhw_tx_timestamp_dtu_to_rmarker_rctu( + struct mcps802154_local *local, u32 tx_timestamp_dtu, + const struct mcps802154_hrp_uwb_params *hrp_uwb_params, + const struct mcps802154_channel *channel_params, int ant_set_id) { return local->ops->tx_timestamp_dtu_to_rmarker_rctu( - &local->llhw, tx_timestamp_dtu, ant_set_id); + &local->llhw, tx_timestamp_dtu, hrp_uwb_params, channel_params, + ant_set_id); } static inline s64 llhw_difference_timestamp_rctu(struct mcps802154_local *local, @@ -176,16 +178,14 @@ static inline int llhw_set_channel(struct mcps802154_local *local, u8 page, return r; } -static inline int llhw_set_hrp_uwb_params(struct mcps802154_local *local, - int prf, int psr, int sfd_selector, - int phr_rate, int data_rate) +static inline int __nocfi +llhw_set_hrp_uwb_params(struct mcps802154_local *local, + const struct mcps802154_hrp_uwb_params *params) { int r; - trace_llhw_set_hrp_uwb_params(local, prf, psr, sfd_selector, phr_rate, - data_rate); - r = local->ops->set_hrp_uwb_params(&local->llhw, prf, psr, sfd_selector, - phr_rate, data_rate); + trace_llhw_set_hrp_uwb_params(local, params); + r = local->ops->set_hrp_uwb_params(&local->llhw, params); trace_llhw_return_int(local, r); return r; } @@ -289,7 +289,11 @@ llhw_list_calibration(struct mcps802154_local *local) const char *const *r; trace_llhw_list_calibration(local); - r = local->ops->list_calibration(&local->llhw); + if (local->ops->list_calibration) { + r = local->ops->list_calibration(&local->llhw); + } else { + r = NULL; + } trace_llhw_return_void(local); return r; } @@ -309,6 +313,36 @@ static inline int llhw_vendor_cmd(struct mcps802154_local *local, u32 vendor_id, return r; } +static inline int llhw_check_hrp_uwb_params( + struct mcps802154_local *local, + const struct mcps802154_hrp_uwb_params *hrp_uwb_params) +{ + int r; + + trace_llhw_check_hrp_uwb_params(local, hrp_uwb_params); + if (local->ops->check_hrp_uwb_params) + r = local->ops->check_hrp_uwb_params(&local->llhw, + hrp_uwb_params); + else + r = -EOPNOTSUPP; + trace_llhw_return_int(local, r); + return r; +} + +static inline int +llhw_rx_get_measurement(struct mcps802154_local *local, void *rx_ctx, + struct mcps802154_rx_measurement_info *info) +{ + int r; + trace_llhw_rx_get_measurement(local, rx_ctx); + if (local->ops->rx_get_measurement) + r = local->ops->rx_get_measurement(&local->llhw, rx_ctx, info); + else + r = -EOPNOTSUPP; + trace_llhw_return_measurement(local, r, info); + return r; +} + #ifdef CONFIG_MCPS802154_TESTMODE static inline int llhw_testmode_cmd(struct mcps802154_local *local, void *data, int len) diff --git a/mac/mcps_main.c b/mac/mcps_main.c index cde51a0..f3c2590 100644 --- a/mac/mcps_main.c +++ b/mac/mcps_main.c @@ -32,12 +32,9 @@ #include "mcps802154_i.h" #include "llhw-ops.h" #include "default_region.h" -#include "simple_ranging_region.h" +#include "idle_region.h" #include "endless_scheduler.h" #include "on_demand_scheduler.h" -#ifdef CONFIG_MCPS802154_TESTMODE -#include "ping_pong_region.h" -#endif #include "nl.h" #include "warn_return.h" @@ -207,13 +204,16 @@ int mcps802154_get_current_timestamp_dtu(struct mcps802154_llhw *llhw, } EXPORT_SYMBOL(mcps802154_get_current_timestamp_dtu); -u64 mcps802154_tx_timestamp_dtu_to_rmarker_rctu(struct mcps802154_llhw *llhw, - u32 tx_timestamp_dtu, - int ant_set_id) +u64 mcps802154_tx_timestamp_dtu_to_rmarker_rctu( + struct mcps802154_llhw *llhw, u32 tx_timestamp_dtu, + const struct mcps802154_hrp_uwb_params *hrp_uwb_params, + const struct mcps802154_channel *channel_params, int ant_set_id) { struct mcps802154_local *local = llhw_to_local(llhw); return llhw_tx_timestamp_dtu_to_rmarker_rctu(local, tx_timestamp_dtu, + hrp_uwb_params, + channel_params, ant_set_id); } EXPORT_SYMBOL(mcps802154_tx_timestamp_dtu_to_rmarker_rctu); @@ -229,6 +229,15 @@ s64 mcps802154_difference_timestamp_rctu(struct mcps802154_llhw *llhw, } EXPORT_SYMBOL(mcps802154_difference_timestamp_rctu); +int mcps802154_rx_get_measurement(struct mcps802154_llhw *llhw, void *rx_ctx, + struct mcps802154_rx_measurement_info *info) +{ + struct mcps802154_local *local = llhw_to_local(llhw); + + return llhw_rx_get_measurement(local, rx_ctx, info); +} +EXPORT_SYMBOL(mcps802154_rx_get_measurement); + int mcps802154_compute_frame_duration_dtu(struct mcps802154_llhw *llhw, int payload_bytes) { @@ -247,6 +256,16 @@ int mcps802154_vendor_cmd(struct mcps802154_llhw *llhw, u32 vendor_id, } EXPORT_SYMBOL(mcps802154_vendor_cmd); +int mcps802154_check_hrp_uwb_params( + struct mcps802154_llhw *llhw, + const struct mcps802154_hrp_uwb_params *hrp_uwb_params) +{ + struct mcps802154_local *local = llhw_to_local(llhw); + + return llhw_check_hrp_uwb_params(local, hrp_uwb_params); +} +EXPORT_SYMBOL(mcps802154_check_hrp_uwb_params); + struct mcps802154_local *mcps802154_get_first_by_idx(int hw_idx) { struct mcps802154_local *result = NULL, *local; @@ -274,30 +293,24 @@ int __init mcps802154_init(void) return r; r = mcps802154_default_region_init(); WARN_RETURN(r); - r = simple_ranging_region_init(); - WARN_ON(r); + r = mcps802154_idle_region_init(); + WARN_RETURN(r); r = mcps802154_endless_scheduler_init(); WARN_ON(r); r = mcps802154_default_scheduler_init(); WARN_ON(r); r = mcps802154_on_demand_scheduler_init(); WARN_ON(r); -#ifdef CONFIG_MCPS802154_TESTMODE - r = ping_pong_region_init(); - WARN_ON(r); -#endif + return r; } void __exit mcps802154_exit(void) { -#ifdef CONFIG_MCPS802154_TESTMODE - ping_pong_region_exit(); -#endif mcps802154_on_demand_scheduler_exit(); mcps802154_default_scheduler_exit(); mcps802154_endless_scheduler_exit(); - simple_ranging_region_exit(); + mcps802154_idle_region_exit(); mcps802154_default_region_exit(); mcps802154_nl_exit(); } diff --git a/kernel/net/mcps802154/ping_pong_region.h b/mac/mcps_skb_frag.c index c793aa5..6657612 100644 --- a/kernel/net/mcps802154/ping_pong_region.h +++ b/mac/mcps_skb_frag.c @@ -1,7 +1,7 @@ /* * This file is part of the UWB stack for linux. * - * Copyright (c) 2020-2021 Qorvo US, Inc. + * Copyright (c) 2022 Qorvo US, Inc. * * This software is provided under the GNU General Public License, version 2 * (GPLv2), as well as under a Qorvo commercial license. @@ -21,10 +21,13 @@ * Qorvo. Please contact Qorvo to inquire about licensing terms. */ -#ifndef NET_MCPS802154_PING_PONG_REGION_H -#define NET_MCPS802154_PING_PONG_REGION_H +#include <linux/skbuff.h> +#include <linux/module.h> +#include <linux/errno.h> -int ping_pong_region_init(void); -void ping_pong_region_exit(void); - -#endif /* NET_MCPS802154_PING_PONG_REGION_H */ +int mcps_skb_frags_len(struct sk_buff *skb) +{ + /* No fragmentation on Linux. */ + return 0; +} +EXPORT_SYMBOL(mcps_skb_frags_len); diff --git a/mac/nfcc_coex_access.c b/mac/nfcc_coex_access.c index 873cc01..60d6bae 100644 --- a/mac/nfcc_coex_access.c +++ b/mac/nfcc_coex_access.c @@ -40,38 +40,60 @@ static void nfcc_coex_access_done(struct mcps802154_access *access, bool error) struct nfcc_coex_local *local = access_to_local(access); struct nfcc_coex_session *session = &local->session; - if (error) { - const struct dw3000_vendor_cmd_nfcc_coex_get_access_info stop = { + /* Stop on error because the next timestamps is unknown. + * Stop in V2, because the vendor stop is not supported by NFC. */ + if ((error || (session->state == NFCC_COEX_STATE_STOPPING && + session->params.version == 2)) && + !session->get_access_info.watchdog_timeout) { + const struct llhw_vendor_cmd_nfcc_coex_get_access_info stop = { .stop = true, }; local->session.get_access_info = stop; } - if (session->state != NFCC_COEX_STATE_ACCESSING || - session->get_access_info.stop || + if (session->get_access_info.stop || session->get_access_info.watchdog_timeout) - session->started = false; + nfcc_coex_set_state(local, NFCC_COEX_STATE_IDLE); + nfcc_coex_report(local); - nfcc_coex_set_state(local, NFCC_COEX_STATE_IDLE); } static int nfcc_coex_handle(struct mcps802154_access *access) { struct nfcc_coex_local *local = access_to_local(access); struct nfcc_coex_session *session = &local->session; - struct dw3000_vendor_cmd_nfcc_coex_handle_access handle_access = {}; + struct llhw_vendor_cmd_nfcc_coex_handle_access handle_access = {}; handle_access.start = session->first_access; handle_access.timestamp_dtu = access->timestamp_dtu; handle_access.duration_dtu = access->duration_dtu; handle_access.chan = session->params.channel_number; - - nfcc_coex_set_state(local, NFCC_COEX_STATE_ACCESSING); - session->first_access = false; + handle_access.version = session->params.version; + + if (session->state == NFCC_COEX_STATE_STOPPING && + session->params.version == 3) { + /* Stop processing : stop the nfcc coex */ + if (local->session.first_access) { + struct mcps802154_region_demand *rd = + &session->region_demand; + struct llhw_vendor_cmd_nfcc_coex_stop stop = { + .timestamp_dtu = rd->timestamp_dtu, + .duration_dtu = rd->max_duration_dtu, + .version = session->params.version, + }; + return mcps802154_vendor_cmd( + local->llhw, VENDOR_QORVO_OUI, + LLHW_VENDOR_CMD_NFCC_COEX_STOP, &stop, + sizeof(stop)); + } else + return mcps802154_vendor_cmd( + local->llhw, VENDOR_QORVO_OUI, + LLHW_VENDOR_CMD_NFCC_COEX_STOP, NULL, 0); + } return mcps802154_vendor_cmd(local->llhw, VENDOR_QORVO_OUI, - DW3000_VENDOR_CMD_NFCC_COEX_HANDLE_ACCESS, + LLHW_VENDOR_CMD_NFCC_COEX_HANDLE_ACCESS, &handle_access, sizeof(handle_access)); } @@ -79,14 +101,16 @@ static int nfcc_coex_tx_done(struct mcps802154_access *access) { struct nfcc_coex_local *local = access_to_local(access); struct nfcc_coex_session *session = &local->session; - struct dw3000_vendor_cmd_nfcc_coex_get_access_info *get_access_info = + struct llhw_vendor_cmd_nfcc_coex_get_access_info *get_access_info = &session->get_access_info; struct mcps802154_region_demand *rd = &session->region_demand; int r; + session->first_access = false; + r = mcps802154_vendor_cmd( local->llhw, VENDOR_QORVO_OUI, - DW3000_VENDOR_CMD_NFCC_COEX_GET_ACCESS_INFORMATION, + LLHW_VENDOR_CMD_NFCC_COEX_GET_ACCESS_INFORMATION, get_access_info, sizeof(*get_access_info)); if (r) return r; @@ -98,12 +122,17 @@ static int nfcc_coex_tx_done(struct mcps802154_access *access) return 1; } -static int nfcc_coex_schedule_change(struct mcps802154_access *access) +static int nfcc_coex_broken(struct mcps802154_access *access) { struct nfcc_coex_local *local = access_to_local(access); - struct nfcc_coex_session *session = &local->session; + const struct llhw_vendor_cmd_nfcc_coex_get_access_info + watchdog_timeout = { + .watchdog_timeout = true, + }; - return session->state == NFCC_COEX_STATE_STOPPING ? 1 : 0; + local->session.get_access_info = watchdog_timeout; + /* Request end of current access. */ + return -ETIME; } struct mcps802154_access_vendor_ops nfcc_coex_ops = { @@ -112,7 +141,7 @@ struct mcps802154_access_vendor_ops nfcc_coex_ops = { }, .handle = nfcc_coex_handle, .tx_done = nfcc_coex_tx_done, - .schedule_change = nfcc_coex_schedule_change, + .broken = nfcc_coex_broken, }; static struct mcps802154_access * @@ -140,7 +169,8 @@ struct mcps802154_access *nfcc_coex_get_access(struct mcps802154_region *region, struct nfcc_coex_local *local = region_to_local(region); struct nfcc_coex_session *session = &local->session; - if (session->started) { + if (session->state == NFCC_COEX_STATE_STARTED || + session->state == NFCC_COEX_STATE_STOPPING) { nfcc_coex_session_update(local, session, next_timestamp_dtu, region_duration_dtu); return nfcc_coex_access_controller(local, session); diff --git a/mac/nfcc_coex_region.c b/mac/nfcc_coex_region.c index bbd12df..1e26b64 100644 --- a/mac/nfcc_coex_region.c +++ b/mac/nfcc_coex_region.c @@ -64,16 +64,8 @@ static void nfcc_coex_close(struct mcps802154_region *region) static void nfcc_coex_notify_stop(struct mcps802154_region *region) { struct nfcc_coex_local *local = region_to_local(region); - struct nfcc_coex_session *session = &local->session; trace_region_nfcc_coex_notify_stop(local); - nfcc_coex_session_control(local, NFCC_COEX_CALL_CCC_SESSION_STOP, NULL, - NULL); - if (session->started) { - pr_err("device stopped while nfcc coex not stopped state=%d", - local->session.state); - session->started = false; - } } static int nfcc_coex_call(struct mcps802154_region *region, u32 call_id, @@ -100,44 +92,30 @@ static int nfcc_coex_get_demand(struct mcps802154_region *region, const struct nfcc_coex_session *session = &local->session; const struct mcps802154_region_demand *rd = &session->region_demand; - trace_region_nfcc_coex_get_demand(local, next_timestamp_dtu, rd); - if (!session->started) - return 0; + demand->max_duration_dtu = 0; + + switch (session->state) { + case NFCC_COEX_STATE_STARTED: + if (is_before_dtu(rd->timestamp_dtu, next_timestamp_dtu)) + demand->timestamp_dtu = next_timestamp_dtu; + else + demand->timestamp_dtu = rd->timestamp_dtu; + return 1; + + case NFCC_COEX_STATE_STOPPING: + if (session->first_access) { + if (is_before_dtu(rd->timestamp_dtu, + next_timestamp_dtu)) + demand->timestamp_dtu = next_timestamp_dtu; + else + demand->timestamp_dtu = rd->timestamp_dtu; + } else + demand->timestamp_dtu = next_timestamp_dtu; + return 1; - if (is_before_dtu(rd->timestamp_dtu, next_timestamp_dtu)) { - /* Date is late. */ - int shift_dtu = next_timestamp_dtu - rd->timestamp_dtu; - int new_duration_dtu = rd->max_duration_dtu - shift_dtu; - - new_duration_dtu = - new_duration_dtu <= 0 ? 1 : new_duration_dtu; - /* Keep 'rd' unchanged, because the update will be done - * during the get_access. - * See nfcc_coex_session_update function. */ - demand->timestamp_dtu = next_timestamp_dtu; - demand->max_duration_dtu = new_duration_dtu; - } else if (!rd->max_duration_dtu) { - /* Infinite duration will lock the region - * interleaving. - * Duration value can be 0 when the region is started - * when an another region have been started. - * In other words, the get_demand will be call - * before the get_access/access_done. - * - * Remarks: - * - The duration_dtu must stay at 0, which is - * forward to nfcc_coex_access_controller and - * nfcc_coex_handle functions. - * - 12ms is an default value returned which sess_dbg done - * on nfcc initiator board (it's a workaround). - **/ - demand->timestamp_dtu = rd->timestamp_dtu; - demand->max_duration_dtu = - 12 * (local->llhw->dtu_freq_hz / 1000); - } else { - memcpy(demand, rd, sizeof(*demand)); + default: + return 0; } - return 1; } void nfcc_coex_set_state(struct nfcc_coex_local *local, @@ -152,8 +130,8 @@ void nfcc_coex_set_state(struct nfcc_coex_local *local, void nfcc_coex_report(struct nfcc_coex_local *local) { struct nfcc_coex_session *session = &local->session; - const struct dw3000_vendor_cmd_nfcc_coex_get_access_info - *get_access_info = &session->get_access_info; + const struct llhw_vendor_cmd_nfcc_coex_get_access_info *get_access_info = + &session->get_access_info; struct sk_buff *msg; int r; diff --git a/mac/nfcc_coex_region_call.c b/mac/nfcc_coex_region_call.c index bf58da1..743b581 100644 --- a/mac/nfcc_coex_region_call.c +++ b/mac/nfcc_coex_region_call.c @@ -42,6 +42,8 @@ static const struct nla_policy nfcc_coex_session_param_nla_policy [NFCC_COEX_CCC_SESSION_PARAM_ATTR_MAX + 1] = { [NFCC_COEX_CCC_SESSION_PARAM_ATTR_TIME0_NS] = { .type = NLA_U64 }, [NFCC_COEX_CCC_SESSION_PARAM_ATTR_CHANNEL_NUMBER] = { .type = NLA_U8 }, + [NFCC_COEX_CCC_SESSION_PARAM_ATTR_VERSION] = + NLA_POLICY_RANGE(NLA_U8, 2, 3), }; /** @@ -84,6 +86,7 @@ static int nfcc_coex_session_set_parameters(struct nfcc_coex_local *local, P(TIME0_NS, time0_ns, u64, x); P(CHANNEL_NUMBER, channel_number, u8, x); + P(VERSION, version, u8, x); #undef P @@ -94,7 +97,6 @@ static int nfcc_coex_session_set_parameters(struct nfcc_coex_local *local, if (p->time0_ns - now_ns > max_time0_ns) return -ERANGE; - return 0; } @@ -116,7 +118,7 @@ static int nfcc_coex_session_start(struct nfcc_coex_local *local, s64 diff_dtu; int r; - WARN_ON(session->started); + WARN_ON(session->state == NFCC_COEX_STATE_STARTED); trace_region_nfcc_coex_session_start(local, p); r = mcps802154_get_current_timestamp_dtu(local->llhw, &now_dtu); @@ -132,7 +134,7 @@ static int nfcc_coex_session_start(struct nfcc_coex_local *local, session->region_demand.max_duration_dtu = 0; session->event_portid = info->snd_portid; session->first_access = true; - session->started = true; + nfcc_coex_set_state(local, NFCC_COEX_STATE_STARTED); mcps802154_reschedule(local->llhw); return 0; @@ -162,7 +164,7 @@ static int nfcc_coex_session_start_all(struct nfcc_coex_local *local, if (r) return r; - if (local->session.started) + if (local->session.state == NFCC_COEX_STATE_STARTED) return -EBUSY; nfcc_coex_session_init(local); @@ -189,21 +191,13 @@ static int nfcc_coex_session_start_all(struct nfcc_coex_local *local, static int nfcc_coex_session_stop(struct nfcc_coex_local *local) { struct nfcc_coex_session *session = &local->session; - int r = 0; trace_region_nfcc_coex_session_stop(local); - if (session->started) { - if (session->state == NFCC_COEX_STATE_ACCESSING) { - nfcc_coex_set_state(local, NFCC_COEX_STATE_STOPPING); - r = mcps802154_vendor_cmd( - local->llhw, VENDOR_QORVO_OUI, - DW3000_VENDOR_CMD_NFCC_COEX_STOP, NULL, 0); - if (!r) - /* Access is stopped. */ - mcps802154_reschedule(local->llhw); - } + if (session->state == NFCC_COEX_STATE_STARTED) { + nfcc_coex_set_state(local, NFCC_COEX_STATE_STOPPING); + mcps802154_schedule_invalidate(local->llhw); } - return r; + return 0; } int nfcc_coex_session_control(struct nfcc_coex_local *local, u32 call_id, diff --git a/mac/nfcc_coex_session.c b/mac/nfcc_coex_session.c index 712b470..6247365 100644 --- a/mac/nfcc_coex_session.c +++ b/mac/nfcc_coex_session.c @@ -30,6 +30,9 @@ void nfcc_coex_session_init(struct nfcc_coex_local *local) struct nfcc_coex_session_params *p = &local->session.params; memset(p, 0, sizeof(*p)); + + /* Default protocol version is V2 */ + p->version = 3; } void nfcc_coex_session_update(struct nfcc_coex_local *local, @@ -40,13 +43,10 @@ void nfcc_coex_session_update(struct nfcc_coex_local *local, if (is_before_dtu(rd->timestamp_dtu, next_timestamp_dtu)) { int shift_dtu = next_timestamp_dtu - rd->timestamp_dtu; - int new_duration_dtu = rd->max_duration_dtu - shift_dtu; /* Date is late. */ - new_duration_dtu = new_duration_dtu < 0 ? 0 : new_duration_dtu; - trace_region_nfcc_coex_session_update_late(local, shift_dtu, - new_duration_dtu); + trace_region_nfcc_coex_session_update_late(local, shift_dtu, 0); rd->timestamp_dtu = next_timestamp_dtu; - rd->max_duration_dtu = new_duration_dtu; + rd->max_duration_dtu = 0; } } diff --git a/mac/nfcc_coex_session.h b/mac/nfcc_coex_session.h index a3cad45..49de0b3 100644 --- a/mac/nfcc_coex_session.h +++ b/mac/nfcc_coex_session.h @@ -42,20 +42,24 @@ struct nfcc_coex_session_params { * @channel_number: Channel to use for the session, 5 or 9. */ u8 channel_number; + /** + * @version: Protocol version to use. + */ + u8 version; }; /** * enum nfcc_coex_state - State of the unique session. * @NFCC_COEX_STATE_IDLE: * Session is not used by access right now. - * @NFCC_COEX_STATE_ACCESSING: - * Session is currently used on an access. + * @NFCC_COEX_STATE_STARTED: + * Session is started. * @NFCC_COEX_STATE_STOPPING: * Session is currently used for the last access. */ enum nfcc_coex_state { NFCC_COEX_STATE_IDLE, - NFCC_COEX_STATE_ACCESSING, + NFCC_COEX_STATE_STARTED, NFCC_COEX_STATE_STOPPING, }; @@ -75,7 +79,7 @@ struct nfcc_coex_session { /** * @get_access_info: Next access feedback get through a vendor command. */ - struct dw3000_vendor_cmd_nfcc_coex_get_access_info get_access_info; + struct llhw_vendor_cmd_nfcc_coex_get_access_info get_access_info; /** * @region_demand: Region access demand which contains start and duration. */ @@ -88,10 +92,6 @@ struct nfcc_coex_session { * @state: State of the unique session. */ enum nfcc_coex_state state; - /** - * @started: Session is currently started. - */ - bool started; }; /* Forward declaration. */ diff --git a/mac/nfcc_coex_trace.h b/mac/nfcc_coex_trace.h index 4753e7a..3d24f81 100644 --- a/mac/nfcc_coex_trace.h +++ b/mac/nfcc_coex_trace.h @@ -52,10 +52,10 @@ TRACE_DEFINE_ENUM(NFCC_COEX_CALL_CCC_SESSION_NOTIFICATION); } #define NFCC_COEX_STATE_SYMBOLS \ nfcc_coex_state_name(IDLE), \ - nfcc_coex_state_name(ACCESSING), \ + nfcc_coex_state_name(STARTED), \ nfcc_coex_state_name(STOPPING) TRACE_DEFINE_ENUM(NFCC_COEX_STATE_IDLE); -TRACE_DEFINE_ENUM(NFCC_COEX_STATE_ACCESSING); +TRACE_DEFINE_ENUM(NFCC_COEX_STATE_STARTED); TRACE_DEFINE_ENUM(NFCC_COEX_STATE_STOPPING); #define NFCC_COEX_LOCAL_ENTRY __field(enum nfcc_coex_state, state) @@ -86,15 +86,17 @@ TRACE_EVENT( NFCC_COEX_LOCAL_ENTRY __field(u64, time0_ns) __field(u8, channel_number) + __field(u8, version) ), TP_fast_assign( NFCC_COEX_LOCAL_ASSIGN; __entry->time0_ns = p->time0_ns; __entry->channel_number = p->channel_number; + __entry->version = p->version; ), - TP_printk(NFCC_COEX_LOCAL_PR_FMT " time0_ns=%llu channel_number=%d", + TP_printk(NFCC_COEX_LOCAL_PR_FMT " time0_ns=%llu channel_number=%d version=%d", NFCC_COEX_LOCAL_PR_ARG, __entry->time0_ns, - __entry->channel_number) + __entry->channel_number, __entry->version) ); DEFINE_EVENT( @@ -128,32 +130,6 @@ TRACE_EVENT( ); TRACE_EVENT( - region_nfcc_coex_get_demand, - TP_PROTO(const struct nfcc_coex_local *local, - u32 next_timestamp_dtu, - const struct mcps802154_region_demand *rd), - TP_ARGS(local, next_timestamp_dtu, rd), - TP_STRUCT__entry( - NFCC_COEX_LOCAL_ENTRY - __field(u32, next_timestamp_dtu) - __field(u32, timestamp_dtu) - __field(int, duration_dtu) - ), - TP_fast_assign( - NFCC_COEX_LOCAL_ASSIGN; - __entry->next_timestamp_dtu = next_timestamp_dtu; - __entry->timestamp_dtu = rd->timestamp_dtu; - __entry->duration_dtu = rd->max_duration_dtu; - ), - TP_printk(NFCC_COEX_LOCAL_PR_FMT " next_timestamp_dtu=0x%08x " - "rd.timestamp_dtu=0x%08x rd.duration_dtu=0x%08x", - NFCC_COEX_LOCAL_PR_ARG, - __entry->next_timestamp_dtu, - __entry->timestamp_dtu, - __entry->duration_dtu) -); - -TRACE_EVENT( region_nfcc_coex_session_update_late, TP_PROTO(const struct nfcc_coex_local *local, int shift_dtu, int new_duration_dtu), @@ -197,7 +173,7 @@ TRACE_EVENT( TRACE_EVENT( region_nfcc_coex_report, TP_PROTO(const struct nfcc_coex_local *local, - const struct dw3000_vendor_cmd_nfcc_coex_get_access_info *info), + const struct llhw_vendor_cmd_nfcc_coex_get_access_info *info), TP_ARGS(local, info), TP_STRUCT__entry( NFCC_COEX_LOCAL_ENTRY diff --git a/mac/on_demand_scheduler.c b/mac/on_demand_scheduler.c index 4cf2b24..852901e 100644 --- a/mac/on_demand_scheduler.c +++ b/mac/on_demand_scheduler.c @@ -47,6 +47,10 @@ struct mcps802154_on_demand_local { * @llhw: Low layer hardware attached. */ struct mcps802154_llhw *llhw; + /** + * @idle_region: Idle region to delay start of region selected. + */ + struct mcps802154_region *idle_region; }; static inline struct mcps802154_on_demand_local * @@ -63,10 +67,20 @@ mcps802154_on_demand_scheduler_open(struct mcps802154_llhw *llhw) plocal = kmalloc(sizeof(*plocal), GFP_KERNEL); if (!plocal) - return NULL; + goto open_failure; + + plocal->idle_region = mcps802154_region_open(llhw, "idle", NULL, NULL); + if (!plocal->idle_region) { + goto open_failure; + } + plocal->llhw = llhw; plocal->scheduler.n_regions = 0; return &plocal->scheduler; + +open_failure: + kfree(plocal); + return NULL; } static void @@ -75,28 +89,27 @@ mcps802154_on_demand_scheduler_close(struct mcps802154_scheduler *scheduler) struct mcps802154_on_demand_local *plocal = scheduler_to_plocal(scheduler); + kfree(plocal->idle_region); kfree(plocal); } -static int mcps802154_on_demand_scheduler_update_schedule( - struct mcps802154_scheduler *scheduler, - const struct mcps802154_schedule_update *schedule_update, - u32 next_timestamp_dtu) +static int mcps802154_on_demand_scheduler_get_next_region( + struct mcps802154_on_demand_local *plocal, struct list_head *regions, + const struct mcps802154_region *first_region, u32 next_timestamp_dtu, + struct mcps802154_region_demand *next_demand, + struct mcps802154_region **next_region) { - struct mcps802154_on_demand_local *plocal = - scheduler_to_plocal(scheduler); - struct mcps802154_region_demand demand; - struct mcps802154_region *region, *next_region = NULL; - struct list_head *regions; + struct mcps802154_region *region; int max_duration_dtu = 0; - u32 start_dtu; int r; - mcps802154_schedule_get_regions(plocal->llhw, ®ions); - + *next_region = NULL; list_for_each_entry (region, regions, ca_entry) { struct mcps802154_region_demand candidate = {}; + if (first_region && region == first_region) + continue; + r = mcps802154_region_get_demand( plocal->llhw, region, next_timestamp_dtu, &candidate); switch (r) { @@ -121,30 +134,51 @@ static int mcps802154_on_demand_scheduler_update_schedule( next_timestamp_dtu; /* Arbitrate between regions. */ - if (!next_region || is_before_dtu(candidate.timestamp_dtu, - demand.timestamp_dtu)) { - next_region = region; - demand = candidate; + if (!*next_region || + is_before_dtu(candidate.timestamp_dtu, + next_demand->timestamp_dtu)) { + *next_region = region; + *next_demand = candidate; /* Is there some time remaining for a region with * less priority? */ if (!is_before_dtu(next_timestamp_dtu, - demand.timestamp_dtu)) + next_demand->timestamp_dtu)) break; else - max_duration_dtu = demand.timestamp_dtu - + max_duration_dtu = next_demand->timestamp_dtu - next_timestamp_dtu; } } + return *next_region ? 1 : 0; +} + +static int mcps802154_on_demand_scheduler_update_schedule( + struct mcps802154_scheduler *scheduler, + const struct mcps802154_schedule_update *schedule_update, + u32 next_timestamp_dtu) +{ + struct mcps802154_on_demand_local *plocal = + scheduler_to_plocal(scheduler); + struct list_head *regions; + struct mcps802154_region_demand next_demand; + struct mcps802154_region *next_region = NULL; + u32 start_in_schedule_dtu; + int r; + + mcps802154_schedule_get_regions(plocal->llhw, ®ions); + r = mcps802154_on_demand_scheduler_get_next_region( + plocal, regions, NULL, next_timestamp_dtu, &next_demand, + &next_region); + if (r < 0) + return r; + if (!next_region) return -ENOENT; - start_dtu = demand.timestamp_dtu - - schedule_update->expected_start_timestamp_dtu; + start_in_schedule_dtu = next_demand.timestamp_dtu - next_timestamp_dtu; - r = mcps802154_schedule_set_start( - schedule_update, schedule_update->expected_start_timestamp_dtu); - /* Can not fail, only possible error is invalid parameters. */ + r = mcps802154_schedule_set_start(schedule_update, next_timestamp_dtu); WARN_RETURN(r); r = mcps802154_schedule_recycle(schedule_update, 0, @@ -152,12 +186,81 @@ static int mcps802154_on_demand_scheduler_update_schedule( /* Can not fail, only possible error is invalid parameters. */ WARN_RETURN(r); + if (next_demand.max_duration_dtu) + next_demand.max_duration_dtu += start_in_schedule_dtu; + start_in_schedule_dtu = 0; + + if (start_in_schedule_dtu) + /* Don't give the access to the region too early. + * And provide advantages: + * - to have a region inserted with a CA invalidate schedule. + * - Reduce latency with TX frame prepared close to region + * start date. */ + r = mcps802154_schedule_add_region(schedule_update, + plocal->idle_region, 0, + start_in_schedule_dtu, + false); r = mcps802154_schedule_add_region(schedule_update, next_region, - start_dtu, demand.max_duration_dtu); + start_in_schedule_dtu, + next_demand.max_duration_dtu, true); return r; } +static int mcps802154_on_demand_scheduler_get_next_demands( + struct mcps802154_scheduler *scheduler, + const struct mcps802154_region *region, u32 timestamp_dtu, + int duration_dtu, int delta_dtu, + struct mcps802154_region_demand *demands) +{ + struct mcps802154_on_demand_local *plocal = + scheduler_to_plocal(scheduler); + struct list_head *regions; + bool is_demands_set = false; + u32 next_timestamp_dtu = timestamp_dtu; + int r; + + mcps802154_schedule_get_regions(plocal->llhw, ®ions); + + while (true) { + struct mcps802154_region_demand next_demand; + struct mcps802154_region *next_region = NULL; + + r = mcps802154_on_demand_scheduler_get_next_region( + plocal, regions, region, next_timestamp_dtu, + &next_demand, &next_region); + if (r < 0) + return r; + if (!r || !next_demand.max_duration_dtu || + !is_before_dtu(next_demand.timestamp_dtu, + timestamp_dtu + duration_dtu)) + break; + if (!is_demands_set) { + *demands = next_demand; + is_demands_set = true; + } else if (!is_before_dtu(demands->timestamp_dtu + + demands->max_duration_dtu + + delta_dtu, + next_demand.timestamp_dtu)) { + demands->max_duration_dtu = + next_demand.timestamp_dtu + + next_demand.max_duration_dtu - + demands->timestamp_dtu; + } else { + break; + } + + if (!is_before_dtu(demands->timestamp_dtu + + demands->max_duration_dtu, + timestamp_dtu + duration_dtu)) + break; + + next_timestamp_dtu = + demands->timestamp_dtu + demands->max_duration_dtu; + } + return is_demands_set ? 1 : 0; +} + static struct mcps802154_scheduler_ops mcps802154_on_demand_scheduler_scheduler = { .owner = THIS_MODULE, @@ -167,6 +270,8 @@ static struct mcps802154_scheduler_ops .set_parameters = NULL, /* No scheduler parameters for now. */ .update_schedule = mcps802154_on_demand_scheduler_update_schedule, + .get_next_demands = + mcps802154_on_demand_scheduler_get_next_demands, }; int __init mcps802154_on_demand_scheduler_init(void) diff --git a/mac/pctt_access.c b/mac/pctt_access.c index 69747ec..ae7b994 100644 --- a/mac/pctt_access.c +++ b/mac/pctt_access.c @@ -21,6 +21,7 @@ * Qorvo. Please contact Qorvo to inquire about licensing terms. */ +#include <linux/math64.h> #include "pctt_access.h" #include "pctt_region.h" #include "pctt_region_call.h" @@ -32,18 +33,34 @@ #include <net/pctt_region_params.h> #include <asm/unaligned.h> +#include "warn_return.h" + #define PCTT_STS_FOM_THRESHOLD 153 +/* The FC-PHY shall have a block timing tolerance of +/-100 ppm as + specified in IEEE Std 802.15.4z-2020, subclause 6.9.7.2. */ +#define PCTT_MARGIN_PPM 200 + +static inline int pctt_rx_margin(int duration) +{ + return duration / (1000000 / PCTT_MARGIN_PPM); +} -static void pctt_set_sts_params(struct mcps802154_sts_params *sts_params, - u32 sts_index) +static void +pctt_set_sts_params(struct mcps802154_sts_params *sts_params, + const struct pctt_session_params *session_params) { const u8 key[AES_KEYSIZE_128] = { 0x14, 0x14, 0x86, 0x74, 0xd1, 0xd3, 0x36, 0xaa, 0xf8, 0x60, 0x50, 0xa8, 0x14, 0xeb, 0x22, 0xf }; u8 *iv = sts_params->v; - - sts_params->n_segs = 1; - sts_params->seg_len = 64; + u8 seg_len = session_params->sts_length == PCTT_STS_LENGTH_128 ? + 128 : + session_params->sts_length == PCTT_STS_LENGTH_32 ? + 32 : + 64; + + sts_params->n_segs = session_params->number_of_sts_segments; + sts_params->seg_len = seg_len; sts_params->sp2_tx_gap_4chips = 0; sts_params->sp2_rx_gap_4chips[0] = 0; sts_params->sp2_rx_gap_4chips[1] = 0; @@ -52,11 +69,31 @@ static void pctt_set_sts_params(struct mcps802154_sts_params *sts_params, /* Overflow is not propagated to the next IV */ put_unaligned_be32(0x362eeb34u, &iv[0]); - put_unaligned_be32(0xc44fa8fbu + sts_index, &iv[sizeof(u32)]); + put_unaligned_be32(0xc44fa8fbu + session_params->sts_index, + &iv[sizeof(u32)]); put_unaligned_be64(0xd37ec3ca1f9a3de4ull, &iv[sizeof(u64)]); memcpy(sts_params->key, key, AES_KEYSIZE_128); } +static void pctt_randomize_psdu(struct pctt_local *local) +{ + struct pctt_session *session = &local->session; + struct pctt_session_params *p = &session->params; + + if (p->randomize_psdu && session->first_access) { + const int A = 1664525, B = 1013904223; + /* First byte of data is used as seed. */ + u32 state = p->data_payload[0]; + u8 *buf = p->data_payload; + int size = p->data_payload_len; + int i; + for (i = 0; i < size; i++) { + state = A * state + B; + buf[i] = state >> 8; + } + } +} + /** * pctt_access_setup_frame() - Fill an access frame from a PCTT slot. * @local: PCTT context. @@ -77,26 +114,26 @@ static void pctt_access_setup_frame(struct pctt_local *local, bool is_rframe = p->rframe_config != PCTT_RFRAME_CONFIG_SP0; if (is_rframe) { - pctt_set_sts_params(sts_params, p->sts_index); + pctt_set_sts_params(sts_params, p); sts_params_for_access = sts_params; } if (slot->is_tx) { u8 flags = slot->is_immediate ? 0 : - MCPS802154_TX_FRAME_TIMESTAMP_DTU; + MCPS802154_TX_FRAME_CONFIG_TIMESTAMP_DTU; if (is_rframe) { if (p->rframe_config == PCTT_RFRAME_CONFIG_SP3) - flags |= MCPS802154_TX_FRAME_SP3; + flags |= MCPS802154_TX_FRAME_CONFIG_SP3; else if (p->rframe_config == PCTT_RFRAME_CONFIG_SP2) - flags |= MCPS802154_TX_FRAME_SP2; + flags |= MCPS802154_TX_FRAME_CONFIG_SP2; else - flags |= MCPS802154_TX_FRAME_SP1; + flags |= MCPS802154_TX_FRAME_CONFIG_SP1; } *frame = (struct mcps802154_access_frame){ .is_tx = true, - .tx_frame_info = { + .tx_frame_config = { .timestamp_dtu = frame_dtu, .flags = flags, .ant_set_id = p->tx_antenna_selection, @@ -106,24 +143,31 @@ static void pctt_access_setup_frame(struct pctt_local *local, } else { u8 flags = slot->is_immediate ? 0 : - MCPS802154_RX_INFO_TIMESTAMP_DTU; - u16 request = MCPS802154_RX_FRAME_INFO_TIMESTAMP_RCTU; + MCPS802154_RX_FRAME_CONFIG_TIMESTAMP_DTU; + u16 request = MCPS802154_RX_FRAME_INFO_TIMESTAMP_RCTU | + MCPS802154_RX_FRAME_INFO_TIMESTAMP_DTU | + MCPS802154_RX_FRAME_INFO_RSSI; if (is_rframe) { - flags |= MCPS802154_RX_INFO_RANGING; request |= MCPS802154_RX_FRAME_INFO_RANGING_STS_FOM; - + flags |= MCPS802154_RX_FRAME_CONFIG_RANGING; + if (session->cmd_id == PCTT_ID_ATTR_SS_TWR) { + flags |= + MCPS802154_RX_FRAME_CONFIG_RANGING_PDOA; + request |= + MCPS802154_RX_FRAME_INFO_RANGING_PDOA; + } if (p->rframe_config == PCTT_RFRAME_CONFIG_SP3) - flags |= MCPS802154_RX_INFO_SP3; + flags |= MCPS802154_RX_FRAME_CONFIG_SP3; else if (p->rframe_config == PCTT_RFRAME_CONFIG_SP2) - flags |= MCPS802154_RX_INFO_SP2; + flags |= MCPS802154_RX_FRAME_CONFIG_SP2; else - flags |= MCPS802154_RX_INFO_SP1; + flags |= MCPS802154_RX_FRAME_CONFIG_SP1; } *frame = (struct mcps802154_access_frame){ .is_tx = false, .rx = { - .info = { + .frame_config = { .timestamp_dtu = frame_dtu, .flags = flags, .timeout_dtu = slot->timeout_dtu, @@ -140,15 +184,15 @@ static struct sk_buff *pctt_tx_get_frame(struct mcps802154_access *access, int frame_idx) { struct pctt_local *local = access_to_local(access); + struct pctt_session *session = &local->session; + const struct pctt_session_params *p = &session->params; struct sk_buff *skb = NULL; - if (local->data_payload_len) { - /* FIXME: Which size is the good one? - * - 1024, - * - local->data_payload_len, - * - PCTT_PAYLOAD_MAX_LEN(4096). */ - skb = mcps802154_frame_alloc(local->llhw, 1024, GFP_KERNEL); - skb_put_data(skb, local->data_payload, local->data_payload_len); + if (p->data_payload_len) { + skb = mcps802154_frame_alloc(local->llhw, p->data_payload_len, + GFP_KERNEL); + if (skb) + skb_put_data(skb, p->data_payload, p->data_payload_len); } return skb; @@ -171,11 +215,13 @@ static void pctt_tx_return(struct mcps802154_access *access, int frame_idx, static bool pctt_rx_sts_good(const struct mcps802154_rx_frame_info *i) { + int idx; if (!(i->flags & MCPS802154_RX_FRAME_INFO_RANGING_STS_FOM)) return false; - /* Only one segment for the moment. */ - if (i->ranging_sts_fom[0] < PCTT_STS_FOM_THRESHOLD) - return false; + for (idx = 0; idx < MCPS802154_STS_N_SEGS_MAX; idx++) { + if (i->ranging_sts_fom[idx] < PCTT_STS_FOM_THRESHOLD) + return false; + } return true; } @@ -193,6 +239,34 @@ static void pctt_rx_frame_ss_twr(struct pctt_local *local, PCTT_STATUS_RANGING_RX_PHY_TOA_FAILED; return; } + if (!pctt_rx_sts_good(info)) { + local->results.status = + PCTT_STATUS_RANGING_RX_PHY_STS_FAILED; + return; + } + if (info->flags & MCPS802154_RX_FRAME_INFO_RANGING_PDOA) { + struct mcps802154_rx_measurement_info info = {}; + int r; + + info.flags |= MCPS802154_RX_MEASUREMENTS_AOAS; + r = mcps802154_rx_get_measurement(local->llhw, NULL, + &info); + if (!r && + info.flags & MCPS802154_RX_MEASUREMENTS_AOAS && + info.n_aoas) { + /* TODO: Find which aoas index to use */ + ss_twr->pdoa_azimuth_deg_q7 = + map_rad_q11_to_deg_q7( + info.aoas[0].pdoa_rad_q11); + ss_twr->aoa_azimuth_deg_q7 = + map_rad_q11_to_deg_q7( + info.aoas[0].aoa_rad_q11); + } + } + + if (info->flags & MCPS802154_RX_FRAME_INFO_RSSI) { + ss_twr->rssi = info->rssi; + } ss_twr->rx_timestamps_rctu = info->timestamp_rctu; @@ -218,6 +292,7 @@ static void pctt_rx_frame_ss_twr(struct pctt_local *local, ss_twr->tx_timestamps_rctu = mcps802154_tx_timestamp_dtu_to_rmarker_rctu( local->llhw, frame_dtu, + access->hrp_uwb_params, access->channel, p->tx_antenna_selection); pctt_access_setup_frame(local, s, frame_dtu, frame, @@ -240,58 +315,65 @@ static void pctt_rx_frame_ss_twr(struct pctt_local *local, } } -static void pctt_rx_frame_per_rx(struct pctt_local *local, +static void pctt_rx_frame_per_rx(struct pctt_local *local, struct sk_buff *skb, const struct mcps802154_rx_frame_info *info, enum mcps802154_rx_error_type error) { struct pctt_test_per_rx_results *per_rx = &local->results.tests.per_rx; - if (info) { - const struct pctt_session_params *p = &local->session.params; - bool is_rframe = p->rframe_config != PCTT_RFRAME_CONFIG_SP0; + struct pctt_session *session = &local->session; + const struct pctt_session_params *p = &session->params; + bool has_sts = p->rframe_config != PCTT_RFRAME_CONFIG_SP0; - if (is_rframe) { - if (pctt_rx_sts_good(info)) - per_rx->sts_found++; - else - local->results.status = - PCTT_STATUS_RANGING_RX_PHY_STS_FAILED; + if (info) { + if (info->flags & MCPS802154_RX_FRAME_INFO_TIMESTAMP_DTU) { + session->next_timestamp_dtu = info->timestamp_dtu; + session->first_rx_synchronized = true; + } + if (info->flags & MCPS802154_RX_FRAME_INFO_RSSI) { + if (!per_rx->rssi || per_rx->rssi > info->rssi) + per_rx->rssi = info->rssi; } } + session->next_timestamp_dtu += + p->gap_duration_dtu - pctt_rx_margin(p->gap_duration_dtu); switch (error) { case MCPS802154_RX_ERROR_NONE: + case MCPS802154_RX_ERROR_BAD_CKSUM: per_rx->acq_detect++; per_rx->sync_cir_ready++; per_rx->sfd_found++; per_rx->eof++; + if (has_sts && pctt_rx_sts_good(info)) + per_rx->sts_found++; + if (skb && (skb->len != p->data_payload_len || + (!p->randomize_psdu && + memcmp(skb->data, p->data_payload, skb->len)))) + per_rx->psdu_bit_error++; + if (error == MCPS802154_RX_ERROR_BAD_CKSUM) + per_rx->psdu_dec_error++; break; case MCPS802154_RX_ERROR_SFD_TIMEOUT: - per_rx->acq_reject++; - per_rx->sfd_fail++; - break; - case MCPS802154_RX_ERROR_BAD_CKSUM: - per_rx->psdu_bit_error++; - per_rx->eof++; - per_rx->rx_fail++; per_rx->acq_detect++; - per_rx->sync_cir_ready++; - per_rx->sfd_found++; + per_rx->sfd_fail++; break; case MCPS802154_RX_ERROR_UNCORRECTABLE: case MCPS802154_RX_ERROR_FILTERED: case MCPS802154_RX_ERROR_HPDWARN: case MCPS802154_RX_ERROR_OTHER: - per_rx->rx_fail++; + case MCPS802154_RX_ERROR_PHR_DECODE: per_rx->acq_detect++; per_rx->sync_cir_ready++; per_rx->sfd_found++; if (error == MCPS802154_RX_ERROR_OTHER) { - per_rx->phr_dec_error++; per_rx->psdu_dec_error++; + } else if (error == MCPS802154_RX_ERROR_PHR_DECODE) { + per_rx->phr_dec_error++; } break; case MCPS802154_RX_ERROR_TIMEOUT: + per_rx->rx_fail++; break; } } @@ -314,10 +396,14 @@ static void pctt_rx_frame_rx(struct pctt_local *local, struct sk_buff *skb, rx->rx_done_ts_int = (info->timestamp_rctu >> 32) & 0xfffffffe; rx->rx_done_ts_frac = info->timestamp_rctu & 0xffff; - } else + } else { local->results.status = PCTT_STATUS_RANGING_RX_PHY_TOA_FAILED; - } + } + if (info->flags & MCPS802154_RX_FRAME_INFO_RSSI) + rx->rssi = info->rssi; + } else + local->results.status = PCTT_STATUS_RANGING_RX_TIMEOUT; } static void pctt_rx_frame(struct mcps802154_access *access, int frame_idx, @@ -327,13 +413,29 @@ static void pctt_rx_frame(struct mcps802154_access *access, int frame_idx, { struct pctt_local *local = access_to_local(access); struct pctt_session *session = &local->session; + struct llhw_vendor_cmd_pctt_get_frame_info frame_info = {}; local->frames_remaining_nb--; + if (error == MCPS802154_RX_ERROR_BAD_CKSUM) { + struct mcps802154_access_frame *frame = + &access->frames[frame_idx]; + int r; + + frame_info.info.flags = frame->rx.frame_info_flags_request; + r = mcps802154_vendor_cmd(local->llhw, VENDOR_QORVO_OUI, + LLHW_VENDOR_CMD_PCTT_GET_FRAME_INFO, + &frame_info, sizeof(frame_info)); + if (!r) { + skb = frame_info.skb; + info = &frame_info.info; + } + } + if (session->cmd_id == PCTT_ID_ATTR_SS_TWR) pctt_rx_frame_ss_twr(local, info); else if (session->cmd_id == PCTT_ID_ATTR_PER_RX) - pctt_rx_frame_per_rx(local, info, error); + pctt_rx_frame_per_rx(local, skb, info, error); else pctt_rx_frame_rx(local, skb, info); @@ -351,6 +453,7 @@ static void pctt_rx_frame(struct mcps802154_access *access, int frame_idx, case MCPS802154_RX_ERROR_UNCORRECTABLE: case MCPS802154_RX_ERROR_HPDWARN: case MCPS802154_RX_ERROR_OTHER: + case MCPS802154_RX_ERROR_PHR_DECODE: local->results.status = PCTT_STATUS_RANGING_RX_PHY_DEC_FAILED; break; } @@ -371,6 +474,7 @@ static void pctt_access_done(struct mcps802154_access *access, bool error) local->results.status = PCTT_STATUS_RANGING_INTERNAL_ERROR; switch (session->cmd_id) { + case PCTT_ID_ATTR_LOOPBACK: case PCTT_ID_ATTR_SS_TWR: case PCTT_ID_ATTR_RX: end_of_test = true; @@ -389,7 +493,7 @@ static void pctt_access_done(struct mcps802154_access *access, bool error) int r; r = mcps802154_vendor_cmd(local->llhw, VENDOR_QORVO_OUI, - DW3000_VENDOR_CMD_PCTT_SETUP_HW, NULL, + LLHW_VENDOR_CMD_PCTT_SETUP_HW, NULL, 0); if (r) @@ -415,10 +519,11 @@ static struct mcps802154_access * pctt_get_access_periodic_tx(struct pctt_local *local, u32 next_timestamp_dtu) { struct pctt_session *session = &local->session; - const struct pctt_test_params *tp = &session->test_params; + const struct pctt_session_params *p = &session->params; struct mcps802154_access *access = &local->access; struct pctt_slot *s = local->slots; u32 frame_dtu; + access->hrp_uwb_params = &session->hrp_uwb_params; /* Unique frame in this access. */ *s = (struct pctt_slot){ @@ -437,7 +542,9 @@ pctt_get_access_periodic_tx(struct pctt_local *local, u32 next_timestamp_dtu) access->frames = local->frames; access->timestamp_dtu = frame_dtu; /* Compute next transmit date. */ - session->next_timestamp_dtu = frame_dtu + tp->gap_duration_dtu; + session->next_timestamp_dtu = frame_dtu + p->gap_duration_dtu; + + pctt_randomize_psdu(local); return access; } @@ -445,24 +552,123 @@ pctt_get_access_periodic_tx(struct pctt_local *local, u32 next_timestamp_dtu) static struct mcps802154_access * pctt_get_access_per_rx(struct pctt_local *local, u32 next_timestamp_dtu) { + struct pctt_session *session = &local->session; + const struct pctt_session_params *p = &session->params; struct mcps802154_access *access = &local->access; struct pctt_slot *s = local->slots; + u32 frame_timestamp_dtu; + access->hrp_uwb_params = &session->hrp_uwb_params; /* Unique frame in this access. */ *s = (struct pctt_slot){ - .is_immediate = true, - .timeout_dtu = -1, + .is_immediate = !session->first_rx_synchronized, + .timeout_dtu = session->first_rx_synchronized ? + 2 * pctt_rx_margin(p->gap_duration_dtu) : + -1, }; - pctt_access_setup_frame(local, s, next_timestamp_dtu, &local->frames[0], - &local->sts_params[0]); + frame_timestamp_dtu = session->first_rx_synchronized ? + session->next_timestamp_dtu : + next_timestamp_dtu; + + pctt_access_setup_frame(local, s, frame_timestamp_dtu, + &local->frames[0], &local->sts_params[0]); access->ops = &pctt_access_ops; access->method = MCPS802154_ACCESS_METHOD_MULTI; - access->timestamp_dtu = next_timestamp_dtu; - access->duration_dtu = 0; + access->timestamp_dtu = frame_timestamp_dtu; + access->duration_dtu = + session->first_rx_synchronized ? p->gap_duration_dtu : 0; access->n_frames = 1; access->frames = local->frames; + + return access; +} + +static int pctt_handle_loopback(struct mcps802154_access *access) +{ + struct pctt_local *local = access_to_local(access); + struct pctt_session *session = &local->session; + const struct pctt_session_params *p = &session->params; + struct llhw_vendor_cmd_pctt_handle_loopback handle_loopback = {}; + + handle_loopback.ant_set_id = p->tx_antenna_selection; + handle_loopback.data_payload = p->data_payload; + handle_loopback.data_payload_len = p->data_payload_len; + + return mcps802154_vendor_cmd(local->llhw, VENDOR_QORVO_OUI, + LLHW_VENDOR_CMD_PCTT_HANDLE_LOOPBACK, + &handle_loopback, sizeof(handle_loopback)); +} + +static int pctt_tx_done_loopback(struct mcps802154_access *access) +{ + struct pctt_local *local = access_to_local(access); + struct pctt_session *session = &local->session; + const struct pctt_session_params *p = &session->params; + struct llhw_vendor_cmd_pctt_get_loopback_info loopback_info = {}; + int r; + + r = mcps802154_vendor_cmd(local->llhw, VENDOR_QORVO_OUI, + LLHW_VENDOR_CMD_PCTT_GET_LOOPBACK_INFO, + &loopback_info, sizeof(loopback_info)); + if (r) + return r; + + local->results.status = loopback_info.success ? + PCTT_STATUS_RANGING_SUCCESS : + PCTT_STATUS_RANGING_TX_FAILED; + + local->results.tests.loopback.rssi = loopback_info.rssi; + + if (loopback_info.success) { + /* Compare data received with the one sent. */ + struct sk_buff *rx_skb = loopback_info.skb; + WARN_RETURN_ON(!rx_skb, -EFAULT); + + if ((rx_skb->len != p->data_payload_len) || + memcmp(rx_skb->data, p->data_payload, rx_skb->len)) { + local->results.status = PCTT_STATUS_RANGING_TX_FAILED; + } + + /* Free rx_frame skb. */ + kfree_skb(rx_skb); + } + + local->results.tests.loopback.rx_ts_int = + (u32)(loopback_info.rx_timestamp_rctu >> PCTT_TIMESTAMP_SHIFT); + local->results.tests.loopback.rx_ts_frac = + (u16)(loopback_info.rx_timestamp_rctu & + (((unsigned long)1 << PCTT_TIMESTAMP_SHIFT) - 1)); + local->results.tests.loopback.tx_ts_int = + (u32)(loopback_info.tx_timestamp_rctu >> PCTT_TIMESTAMP_SHIFT); + local->results.tests.loopback.tx_ts_frac = + (u16)(loopback_info.tx_timestamp_rctu & + (((unsigned long)1 << PCTT_TIMESTAMP_SHIFT) - 1)); + + /* Request end of current access. */ + return 1; +} + +struct mcps802154_access_vendor_ops pctt_access_ops_loopback = { + .common = { + .access_done = pctt_access_done, + }, + .handle = pctt_handle_loopback, + .tx_done = pctt_tx_done_loopback, +}; + +static struct mcps802154_access * +pctt_get_access_loopback(struct pctt_local *local, u32 next_timestamp_dtu) +{ + struct mcps802154_access *access = &local->access; + + access->method = MCPS802154_ACCESS_METHOD_VENDOR; + access->vendor_ops = &pctt_access_ops_loopback; + access->duration_dtu = 0; + access->timestamp_dtu = next_timestamp_dtu; + access->n_frames = 0; + access->frames = NULL; return access; } @@ -477,6 +683,7 @@ pctt_get_access_ss_twr(struct pctt_local *local, u32 next_timestamp_dtu) int nb_frames; u32 frame_dtu; int i; + access->hrp_uwb_params = &session->hrp_uwb_params; /* First frames. */ *s = (struct pctt_slot){ @@ -497,6 +704,7 @@ pctt_get_access_ss_twr(struct pctt_local *local, u32 next_timestamp_dtu) ss_twr->tx_timestamps_rctu = mcps802154_tx_timestamp_dtu_to_rmarker_rctu( local->llhw, next_timestamp_dtu, + access->hrp_uwb_params, access->channel, p->tx_antenna_selection); } @@ -513,10 +721,6 @@ pctt_get_access_ss_twr(struct pctt_local *local, u32 next_timestamp_dtu) frame_dtu += p->slot_duration_dtu; } - if (!local->frames[0].is_tx) - local->frames[0].rx.frame_info_flags_request |= - MCPS802154_RX_FRAME_INFO_TIMESTAMP_DTU; - access->method = MCPS802154_ACCESS_METHOD_MULTI; access->ops = &pctt_access_ops; access->timestamp_dtu = next_timestamp_dtu; @@ -547,6 +751,9 @@ struct mcps802154_access *pctt_get_access(struct mcps802154_region *region, case PCTT_ID_ATTR_RX: access = pctt_get_access_per_rx(local, next_timestamp_dtu); break; + case PCTT_ID_ATTR_LOOPBACK: + access = pctt_get_access_loopback(local, next_timestamp_dtu); + break; case PCTT_ID_ATTR_SS_TWR: access = pctt_get_access_ss_twr(local, next_timestamp_dtu); break; @@ -562,7 +769,7 @@ struct mcps802154_access *pctt_get_access(struct mcps802154_region *region, int r; r = mcps802154_vendor_cmd(local->llhw, VENDOR_QORVO_OUI, - DW3000_VENDOR_CMD_PCTT_SETUP_HW, + LLHW_VENDOR_CMD_PCTT_SETUP_HW, &session->setup_hw, sizeof(session->setup_hw)); if (r) { diff --git a/mac/pctt_region.c b/mac/pctt_region.c index b6f9699..4c11f5b 100644 --- a/mac/pctt_region.c +++ b/mac/pctt_region.c @@ -70,7 +70,6 @@ static int pctt_call(struct mcps802154_region *region, u32 call_id, case PCTT_CALL_SESSION_GET_PARAMS: return pctt_call_session_get_params(local); case PCTT_CALL_SESSION_SET_PARAMS: - case PCTT_CALL_SET_PARAMS: case PCTT_CALL_SESSION_CMD: return pctt_call_session_control(local, call_id, attrs, info); default: @@ -117,6 +116,7 @@ static int pctt_report_per_rx(struct pctt_local *local, struct sk_buff *msg) P(PSDU_BIT_ERROR, u32, per_rx->psdu_bit_error); P(STS_FOUND, u32, per_rx->sts_found); P(EOF, u32, per_rx->eof); + P(RSSI, u8, per_rx->rssi); #undef P return 0; @@ -144,6 +144,7 @@ static int pctt_report_rx(struct pctt_local *local, struct sk_buff *msg) P(AOA_ELEVATION, s16, rx->aoa_elevation); P(TOA_GAP, u8, rx->toa_gap); P(PHR, u16, rx->phr); + P(RSSI, u8, rx->rssi); P(PSDU_DATA_LEN, u16, rx->psdu_data_len); if (rx->psdu_data_len > 0 && nla_put(msg, PCTT_RESULT_DATA_ATTR_PSDU_DATA, rx->psdu_data_len, @@ -156,6 +157,45 @@ nla_put_failure: return -EMSGSIZE; } +static int pctt_report_loopback(struct pctt_local *local, struct sk_buff *msg) +{ + struct pctt_session *session = &local->session; + const struct pctt_session_params *p = &session->params; + trace_region_pctt_report_loopback(local->results.status); + +#define P(attr, type, value) \ + do { \ + if (nla_put_##type(msg, PCTT_RESULT_DATA_ATTR_##attr, \ + value)) { \ + goto nla_put_failure; \ + } \ + } while (0) + + P(STATUS, u8, local->results.status); + P(RSSI, u8, local->results.tests.loopback.rssi); + P(RX_TS_INT, u32, local->results.tests.loopback.rx_ts_int); + P(RX_TS_FRAC, u16, local->results.tests.loopback.rx_ts_frac); + P(TX_TS_INT, u32, local->results.tests.loopback.tx_ts_int); + P(TX_TS_FRAC, u16, local->results.tests.loopback.tx_ts_frac); + + /* If test succeeded, return data that was sent (and received) as + * PSDU payload. */ + if (!local->results.status) { + P(PSDU_DATA_LEN, u16, p->data_payload_len); + if (nla_put(msg, PCTT_RESULT_DATA_ATTR_PSDU_DATA, + p->data_payload_len, p->data_payload)) { + goto nla_put_failure; + } + } else { + P(PSDU_DATA_LEN, u16, 0); + } +#undef P + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + static int pctt_report_ss_twr(struct pctt_local *local, struct sk_buff *msg) { const struct pctt_test_ss_twr_results *ss_twr = @@ -171,6 +211,11 @@ static int pctt_report_ss_twr(struct pctt_local *local, struct sk_buff *msg) } while (0) P(STATUS, u8, local->results.status); P(MEASUREMENT, u32, ss_twr->measurement_rctu); + P(PDOA_AZIMUTH_DEG_Q7, s16, ss_twr->pdoa_azimuth_deg_q7); + P(PDOA_ELEVATION_DEG_Q7, s16, ss_twr->pdoa_elevation_deg_q7); + P(AOA_AZIMUTH_DEG_Q7, s16, ss_twr->aoa_azimuth_deg_q7); + P(AOA_ELEVATION_DEG_Q7, s16, ss_twr->aoa_elevation_deg_q7); + P(RSSI, u8, ss_twr->rssi); #undef P return 0; @@ -211,6 +256,10 @@ void pctt_report(struct pctt_local *local) if (pctt_report_rx(local, msg)) goto nla_put_failure; break; + case PCTT_ID_ATTR_LOOPBACK: + if (pctt_report_loopback(local, msg)) + goto nla_put_failure; + break; case PCTT_ID_ATTR_SS_TWR: if (pctt_report_ss_twr(local, msg)) goto nla_put_failure; diff --git a/mac/pctt_region.h b/mac/pctt_region.h index 66ce610..8e7830c 100644 --- a/mac/pctt_region.h +++ b/mac/pctt_region.h @@ -34,10 +34,22 @@ #include "pctt_session.h" #define PCTT_SESSION_ID 0 -#define PCTT_PAYLOAD_MAX_LEN 4096 #define PCTT_BOOLEAN_MAX 1 #define PCTT_FRAMES_MAX 2 +#define PCTT_TIMESTAMP_SHIFT 9 +/** + * map_rad_q11_to_deg_q7() - Map a Fixed Point angle to a signed 16-bit integer + * @ang_rad_q11: angle as Q11 fixed_point value in range [-PI, PI] + * + * Return: the angle mapped to deg q7 + */ +static inline s16 map_rad_q11_to_deg_q7(int ang_rad_q11) +{ + /* 180 / (pi * (1 << 4)) => ~3,581. */ + return ang_rad_q11 * 3581 / 1000; +} + /** * struct pctt_test_per_rx_results - PER_RX result for report. */ @@ -94,6 +106,10 @@ struct pctt_test_per_rx_results { * @eof: No. of times end of frame event was triggered. */ u32 eof; + /** + * @rssi: Received signal strength indication (RSSI). + */ + u8 rssi; }; /** @@ -117,10 +133,6 @@ struct pctt_test_rx_results { */ s16 aoa_elevation; /** - * @toa_gap: ToA of main path minus ToA of first path in nanosecond. - */ - u8 toa_gap; - /** * @phr: Received PHR (bits 0-12 as per IEEE spec). */ u16 phr; @@ -129,6 +141,14 @@ struct pctt_test_rx_results { */ u16 psdu_data_len; /** + * @toa_gap: ToA of main path minus ToA of first path in nanosecond. + */ + u8 toa_gap; + /** + * @rssi: Received signal strength indication (RSSI). + */ + u8 rssi; + /** * @psdu_data: Received PSDU Data[0:N] bytes. */ u8 psdu_data[PCTT_PAYLOAD_MAX_LEN]; @@ -151,6 +171,52 @@ struct pctt_test_ss_twr_results { * Treply time of Responder depending on DEVICE_ROLE option. */ u32 measurement_rctu; + /** + * @pdoa_azimuth_deg_q7: Phase Difference of Arrival Azimuth in deg Q7 + */ + s16 pdoa_azimuth_deg_q7; + /** + * @aoa_azimuth_deg_q7: AoA Azimuth in deg Q7 + */ + s16 aoa_azimuth_deg_q7; + /** + * @pdoa_elevation_deg_q7: Phase Difference of Arrival Elevation in deg Q7 + */ + s16 pdoa_elevation_deg_q7; + /** + * @aoa_elevation_deg_q7: AoA Elevation in deg Q7 + */ + s16 aoa_elevation_deg_q7; + /** + * @rssi: Received signal strength indication (RSSI). + */ + u8 rssi; +}; + +/** + * struct pctt_test_loopback_results - LOOPBACK result for report. + */ +struct pctt_test_loopback_results { + /** + * @rssi: Received signal strength indication (RSSI). + */ + u8 rssi; + /** + * @tx_ts_int: Integer part of TX timestamp in 1/124.8 us. resolution. + */ + u32 tx_ts_int; + /** + * @tx_ts_frac: Fractional part of TX timestamp in 1/124.8/512 us. resolution. + */ + u16 tx_ts_frac; + /** + * @rx_ts_int: Integer part of Rx timestamp in 1/124.8 us. resolution. + */ + u32 rx_ts_int; + /** + * @rx_ts_frac: Fractional part of RX timestamp in 1/124.8/512 us. resolution. + */ + u16 rx_ts_frac; }; /** @@ -169,6 +235,10 @@ union pctt_tests_results { * @ss_twr: Result of the SS_TWR command. */ struct pctt_test_ss_twr_results ss_twr; + /** + * @loopback: Result of the LOOPBACK command. + */ + struct pctt_test_loopback_results loopback; }; /** @@ -202,7 +272,7 @@ struct pctt_slot { */ bool is_immediate; /** - * @timeout_dtu: see (mcps802154_rx_info).timeout_dtu. + * @timeout_dtu: see (mcps802154_rx_frame_config).timeout_dtu. */ int timeout_dtu; }; @@ -244,14 +314,6 @@ struct pctt_local { * @frames_remaining_nb: Number of frame remaining to do for the current test. */ int frames_remaining_nb; - /** - * @data_payload: Data to put in TX test frame. - */ - u8 data_payload[PCTT_PAYLOAD_MAX_LEN]; - /** - * @data_payload_len: Length of data to put in TX test frame. - */ - int data_payload_len; }; static inline struct pctt_local * diff --git a/mac/pctt_region_call.c b/mac/pctt_region_call.c index 252e03c..65b60f3 100644 --- a/mac/pctt_region_call.c +++ b/mac/pctt_region_call.c @@ -34,27 +34,13 @@ #include "pctt_trace.h" static const struct nla_policy pctt_call_nla_policy[PCTT_CALL_ATTR_MAX + 1] = { - [PCTT_CALL_ATTR_PARAMS] = { .type = NLA_NESTED }, [PCTT_CALL_ATTR_CMD_ID] = { .type = NLA_U8 }, - [PCTT_CALL_ATTR_CMD_PARAMS] = { .type = NLA_NESTED }, [PCTT_CALL_ATTR_RESULT_DATA] = { .type = NLA_NESTED }, [PCTT_CALL_ATTR_SESSION_ID] = { .type = NLA_U32 }, [PCTT_CALL_ATTR_SESSION_STATE] = { .type = NLA_U8 }, [PCTT_CALL_ATTR_SESSION_PARAMS] = { .type = NLA_NESTED }, }; -static const struct nla_policy pctt_param_nla_policy[PCTT_PARAM_ATTR_MAX + 1] = { - [PCTT_PARAM_ATTR_NUM_PACKETS] = { .type = NLA_U32 }, - [PCTT_PARAM_ATTR_T_GAP] = { .type = NLA_U32 }, - [PCTT_PARAM_ATTR_T_START] = { .type = NLA_U32 }, - [PCTT_PARAM_ATTR_T_WIN] = { .type = NLA_U32 }, - [PCTT_PARAM_ATTR_RANDOMIZE_PSDU] = { .type = NLA_U8 }, - [PCTT_PARAM_ATTR_PHR_RANGING_BIT] = { .type = NLA_U8 }, - [PCTT_PARAM_ATTR_RMARKER_TX_START] = { .type = NLA_U32 }, - [PCTT_PARAM_ATTR_RMARKER_RX_START] = { .type = NLA_U32 }, - [PCTT_PARAM_ATTR_STS_INDEX_AUTO_INCR] = { .type = NLA_U8 }, -}; - static const struct nla_policy pctt_session_param_nla_policy[PCTT_SESSION_PARAM_ATTR_MAX + 1] = { [PCTT_SESSION_PARAM_ATTR_DEVICE_ROLE] = { @@ -74,7 +60,7 @@ static const struct nla_policy pctt_session_param_nla_policy[PCTT_SESSION_PARAM_ }, [PCTT_SESSION_PARAM_ATTR_PRF_MODE] = { .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = PCTT_PRF_MODE_HPRF, + .max = PCTT_PRF_MODE_HPRF_HIGH_RATE, }, [PCTT_SESSION_PARAM_ATTR_PREAMBLE_DURATION] = { .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, @@ -86,12 +72,16 @@ static const struct nla_policy pctt_session_param_nla_policy[PCTT_SESSION_PARAM_ }, [PCTT_SESSION_PARAM_ATTR_NUMBER_OF_STS_SEGMENTS] = { .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, - .max = PCTT_NUMBER_OF_STS_SEGMENTS_2_SEGMENTS, + .max = PCTT_NUMBER_OF_STS_SEGMENTS_4_SEGMENTS, }, [PCTT_SESSION_PARAM_ATTR_PSDU_DATA_RATE] = { .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, .max = PCTT_PSDU_DATA_RATE_31M2, }, + [PCTT_SESSION_PARAM_ATTR_BPRF_PHR_DATA_RATE] = { + .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, + .max = PCTT_PHR_DATA_RATE_6M81, + }, [PCTT_SESSION_PARAM_ATTR_MAC_FCS_TYPE] = { .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, .max = PCTT_MAC_FCS_TYPE_CRC_32, @@ -101,57 +91,25 @@ static const struct nla_policy pctt_session_param_nla_policy[PCTT_SESSION_PARAM_ .max = PCTT_BOOLEAN_MAX, }, [PCTT_SESSION_PARAM_ATTR_STS_INDEX] = { .type = NLA_U32 }, + [PCTT_SESSION_PARAM_ATTR_STS_LENGTH] = { + .type = NLA_U8, .validation_type = NLA_VALIDATE_MAX, + .max = PCTT_STS_LENGTH_128, + }, + [PCTT_SESSION_PARAM_ATTR_NUM_PACKETS] = { .type = NLA_U32 }, + [PCTT_SESSION_PARAM_ATTR_T_GAP] = { .type = NLA_U32 }, + [PCTT_SESSION_PARAM_ATTR_T_START] = { .type = NLA_U32 }, + [PCTT_SESSION_PARAM_ATTR_T_WIN] = { .type = NLA_U32 }, + [PCTT_SESSION_PARAM_ATTR_RANDOMIZE_PSDU] = { .type = NLA_U8 }, + [PCTT_SESSION_PARAM_ATTR_PHR_RANGING_BIT] = { .type = NLA_U8 }, + [PCTT_SESSION_PARAM_ATTR_RMARKER_TX_START] = { .type = NLA_U32 }, + [PCTT_SESSION_PARAM_ATTR_RMARKER_RX_START] = { .type = NLA_U32 }, + [PCTT_SESSION_PARAM_ATTR_STS_INDEX_AUTO_INCR] = { .type = NLA_U8 }, + [PCTT_SESSION_PARAM_ATTR_DATA_PAYLOAD] = { + .type = NLA_BINARY, + .len = PCTT_PAYLOAD_MAX_LEN + }, }; -static const struct nla_policy - pctt_test_param_nla_policy[PCTT_TEST_PARAM_ATTR_MAX + 1] = { - [PCTT_TEST_PARAM_ATTR_PAYLOAD] = { .type = NLA_BINARY, - .len = PCTT_PAYLOAD_MAX_LEN }, - }; - -static int pctt_call_set_params(struct pctt_local *local, - const struct nlattr *params, - const struct genl_info *info) -{ - struct pctt_session *session = &local->session; - struct nlattr *attrs[PCTT_PARAM_ATTR_MAX + 1]; - struct pctt_test_params *tp = &session->test_params; - int r; - - if (!params) - return -EINVAL; - if (session->test_on_going) - return -EBUSY; - - r = nla_parse_nested(attrs, PCTT_PARAM_ATTR_MAX, params, - pctt_param_nla_policy, info->extack); - if (r) - return r; - -#define P(attr, member, type, conv) \ - do { \ - int x; \ - if (attrs[PCTT_PARAM_ATTR_##attr]) { \ - x = nla_get_##type(attrs[PCTT_PARAM_ATTR_##attr]); \ - tp->member = conv; \ - } \ - } while (0) - - P(NUM_PACKETS, num_packets, u32, x); - P(T_GAP, gap_duration_dtu, u32, - ((u64)x * (local->llhw->dtu_freq_hz / 1000)) / 1000); - P(T_START, t_start, u32, x); - P(T_WIN, t_win, u32, x); - P(RANDOMIZE_PSDU, randomize_psdu, u8, x); - P(PHR_RANGING_BIT, phr_ranging_bit, u8, x); - P(RMARKER_TX_START, rmarker_tx_start, u32, x); - P(RMARKER_RX_START, rmarker_rx_start, u32, x); - P(STS_INDEX_AUTO_INCR, sts_index_auto_incr, u8, x); -#undef P - - return 0; -} - int pctt_call_session_get_state(struct pctt_local *local) { struct pctt_session *session = &local->session; @@ -212,14 +170,26 @@ int pctt_call_session_get_params(struct pctt_local *local) P(CHANNEL_NUMBER, channel_number, u8, x); P(PREAMBLE_CODE_INDEX, preamble_code_index, u8, x); P(RFRAME_CONFIG, rframe_config, u8, x); - P(PRF_MODE, prf_mode, u8, x); P(PREAMBLE_DURATION, preamble_duration, u8, x); P(SFD_ID, sfd_id, u8, x); P(NUMBER_OF_STS_SEGMENTS, number_of_sts_segments, u8, x); P(PSDU_DATA_RATE, psdu_data_rate, u8, x); P(MAC_FCS_TYPE, mac_fcs_type, u8, x); + P(PRF_MODE, prf_mode, u8, x); + P(BPRF_PHR_DATA_RATE, phr_data_rate, u8, x); P(TX_ADAPTIVE_PAYLOAD_POWER, tx_adaptive_payload_power, u8, x); P(STS_INDEX, sts_index, u32, x); + P(STS_LENGTH, sts_length, u8, x); + P(NUM_PACKETS, num_packets, u32, x); + P(T_GAP, gap_duration_dtu, u32, + (((u64)x * 1000) / (local->llhw->dtu_freq_hz / 1000))); + P(T_START, t_start, u32, x); + P(T_WIN, t_win, u32, x); + P(RANDOMIZE_PSDU, randomize_psdu, u8, x); + P(PHR_RANGING_BIT, phr_ranging_bit, u8, x); + P(RMARKER_TX_START, rmarker_tx_start, u32, x); + P(RMARKER_RX_START, rmarker_rx_start, u32, x); + P(STS_INDEX_AUTO_INCR, sts_index_auto_incr, u8, x); #undef P nla_nest_end(msg, params); @@ -257,6 +227,17 @@ static int pctt_call_session_set_params(struct pctt_local *local, p->member = conv; \ } \ } while (0) +#define PMEMNCPY(attr, member, size) \ + do { \ + if (attrs[PCTT_SESSION_PARAM_ATTR_##attr]) { \ + struct nlattr *attr = \ + attrs[PCTT_SESSION_PARAM_ATTR_##attr]; \ + int len = nla_len(attr); \ + memcpy(p->member, nla_data(attr), len); \ + p->size = len; \ + } \ + } while (0) + P(DEVICE_ROLE, device_role, u8, x); P(SHORT_ADDR, short_addr, u16, x); P(DESTINATION_SHORT_ADDR, dst_short_addr, u16, x); @@ -267,62 +248,35 @@ static int pctt_call_session_set_params(struct pctt_local *local, P(CHANNEL_NUMBER, channel_number, u8, x); P(PREAMBLE_CODE_INDEX, preamble_code_index, u8, x); P(RFRAME_CONFIG, rframe_config, u8, x); - P(PRF_MODE, prf_mode, u8, x); P(PREAMBLE_DURATION, preamble_duration, u8, x); P(SFD_ID, sfd_id, u8, x); P(NUMBER_OF_STS_SEGMENTS, number_of_sts_segments, u8, x); P(PSDU_DATA_RATE, psdu_data_rate, u8, x); P(MAC_FCS_TYPE, mac_fcs_type, u8, x); + P(PRF_MODE, prf_mode, u8, x); + P(BPRF_PHR_DATA_RATE, phr_data_rate, u8, x); P(TX_ADAPTIVE_PAYLOAD_POWER, tx_adaptive_payload_power, u8, x); P(STS_INDEX, sts_index, u32, x); + P(STS_LENGTH, sts_length, u8, x); + P(NUM_PACKETS, num_packets, u32, x); + P(T_GAP, gap_duration_dtu, u32, + ((u64)x * (local->llhw->dtu_freq_hz / 1000)) / 1000); + P(T_START, t_start, u32, x); + P(T_WIN, t_win, u32, x); + P(RANDOMIZE_PSDU, randomize_psdu, u8, x); + P(PHR_RANGING_BIT, phr_ranging_bit, u8, x); + P(RMARKER_TX_START, rmarker_tx_start, u32, x); + P(RMARKER_RX_START, rmarker_rx_start, u32, x); + P(STS_INDEX_AUTO_INCR, sts_index_auto_incr, u8, x); + PMEMNCPY(DATA_PAYLOAD, data_payload, data_payload_len); +#undef PMEMNCPY #undef P return 0; } -static int pctt_session_set_test_params(struct pctt_local *local, - const struct nlattr *cmd_params_attr, - const struct genl_info *info) -{ - struct pctt_session *session = &local->session; - const struct pctt_session_params *p = &session->params; - struct nlattr *attrs[PCTT_TEST_PARAM_ATTR_MAX + 1]; - struct nlattr *attr; - int r; - - /* Test parameters are not mandatory. */ - if (!cmd_params_attr) - return 0; - - r = nla_parse_nested(attrs, PCTT_TEST_PARAM_ATTR_MAX, cmd_params_attr, - pctt_test_param_nla_policy, info->extack); - if (r) - return r; - - local->data_payload_len = 0; - - attr = attrs[PCTT_TEST_PARAM_ATTR_PAYLOAD]; - if (attr) { - int len = nla_len(attr); - const char *data = nla_data(attr); - - if (local->llhw->hw->flags & IEEE802154_HW_TX_OMIT_CKSUM) - len -= IEEE802154_FCS_LEN; - - if (len > 0) { - if (p->rframe_config == PCTT_RFRAME_CONFIG_SP3) - return -EINVAL; - - memcpy(local->data_payload, data, len); - local->data_payload_len = len; - } - } - return 0; -} - static int pctt_call_cmd(struct pctt_local *local, const struct nlattr *cmd_id_attr, - const struct nlattr *cmd_params_attr, const struct genl_info *info) { struct pctt_session *session = &local->session; @@ -333,8 +287,10 @@ static int pctt_call_cmd(struct pctt_local *local, cmd_id = nla_get_u8(cmd_id_attr); if (session->test_on_going) { - if (cmd_id == PCTT_ID_ATTR_STOP_TEST) { + if (cmd_id == PCTT_ID_ATTR_STOP_TEST && + !session->stop_request) { session->stop_request = true; + mcps802154_reschedule(local->llhw); return 0; } return -EBUSY; @@ -344,9 +300,7 @@ static int pctt_call_cmd(struct pctt_local *local, if (cmd_id == PCTT_ID_ATTR_STOP_TEST) return 0; - r = pctt_session_set_test_params(local, cmd_params_attr, info); - if (r) - return r; + /* FIXME: Used only to detect dw3000_is_active. */ r = mcps802154_get_current_timestamp_dtu(local->llhw, &now_dtu); if (r) @@ -377,13 +331,9 @@ int pctt_call_session_control(struct pctt_local *local, enum pctt_call call_id, if (r) return r; - if (call_id == PCTT_CALL_SET_PARAMS) - return pctt_call_set_params(local, attrs[PCTT_CALL_ATTR_PARAMS], - info); - else if (call_id == PCTT_CALL_SESSION_SET_PARAMS) + if (call_id == PCTT_CALL_SESSION_SET_PARAMS) return pctt_call_session_set_params( local, attrs[PCTT_CALL_ATTR_SESSION_PARAMS], info); else - return pctt_call_cmd(local, attrs[PCTT_CALL_ATTR_CMD_ID], - attrs[PCTT_CALL_ATTR_CMD_PARAMS], info); + return pctt_call_cmd(local, attrs[PCTT_CALL_ATTR_CMD_ID], info); } diff --git a/mac/pctt_session.c b/mac/pctt_session.c index 77bb53a..70beaf1 100644 --- a/mac/pctt_session.c +++ b/mac/pctt_session.c @@ -34,8 +34,8 @@ int pctt_session_init(struct pctt_local *local) struct pctt_session *session = &local->session; struct pctt_session_params *p = &session->params; - /* Do the same behavior as get_session_state in fira. - * INIT state means kzalloc in fira once by session_id. + /* Do the same behavior as get_session_state in FiRa. + * INIT state means kzalloc in FiRa once by session_id. * But as pctt have only one static region. Simulate * kzalloc to do or already done with the local state. */ if (session->state != PCTT_SESSION_STATE_DEINIT) @@ -44,6 +44,9 @@ int pctt_session_init(struct pctt_local *local) memset(p, 0, sizeof(*p)); p->rx_antenna_selection = RX_ANT_SET_ID_DEFAULT; p->tx_antenna_selection = TX_ANT_SET_ID_DEFAULT; + p->preamble_duration = PCTT_PREAMBLE_DURATION_64; + p->preamble_code_index = 9; + p->sts_length = PCTT_STS_LENGTH_64; pctt_session_set_state(local, PCTT_SESSION_STATE_INIT); return 0; } @@ -52,8 +55,8 @@ int pctt_session_deinit(struct pctt_local *local) { struct pctt_session *session = &local->session; - /* Do the same behavior as get_session_state in fira. - * DEINIT state means kfree in fira. + /* Do the same behavior as get_session_state in FiRa. + * DEINIT state means kfree in FiRa. * But as pctt have only one static region. Simulate * kfree to do or already done with the local state. */ if (session->state == PCTT_SESSION_STATE_DEINIT) @@ -81,6 +84,12 @@ int pctt_session_start_test(struct pctt_local *local, enum pctt_id_attrs cmd_id, { struct pctt_session *session = &local->session; const struct pctt_session_params *p = &session->params; + static const enum mcps802154_data_rate pctt_rate_to_mcps_rate[] = { + MCPS802154_DATA_RATE_6M81, + MCPS802154_DATA_RATE_7M80, + MCPS802154_DATA_RATE_27M2, + MCPS802154_DATA_RATE_31M2, + }; trace_region_pctt_session_start_test(cmd_id, p); @@ -88,8 +97,7 @@ int pctt_session_start_test(struct pctt_local *local, enum pctt_id_attrs cmd_id, case PCTT_ID_ATTR_PERIODIC_TX: break; case PCTT_ID_ATTR_LOOPBACK: - /* Not implemented. */ - return -EINVAL; + break; case PCTT_ID_ATTR_SS_TWR: if (p->rframe_config != PCTT_RFRAME_CONFIG_SP3) return -EINVAL; @@ -104,14 +112,70 @@ int pctt_session_start_test(struct pctt_local *local, enum pctt_id_attrs cmd_id, return -EINVAL; } + /* check uwb parameters. */ + if (p->prf_mode == PCTT_PRF_MODE_BPRF) { + if (p->preamble_code_index < 9 || p->preamble_code_index > 24) + return -EINVAL; + if (p->sfd_id != PCTT_SFD_ID_0 && p->sfd_id != PCTT_SFD_ID_2) + return -EINVAL; + if (p->psdu_data_rate != PCTT_PSDU_DATA_RATE_6M81) + return -EINVAL; + if (p->preamble_duration != PCTT_PREAMBLE_DURATION_64) + return -EINVAL; + if (p->number_of_sts_segments > + PCTT_NUMBER_OF_STS_SEGMENTS_1_SEGMENT) + return -EINVAL; + } else { + if (p->preamble_code_index < 25 || p->preamble_code_index > 32) + return -EINVAL; + if (p->sfd_id == PCTT_SFD_ID_0) + return -EINVAL; + if (p->prf_mode == PCTT_PRF_MODE_HPRF && + p->psdu_data_rate > PCTT_PSDU_DATA_RATE_7M80) + return -EINVAL; + if (p->prf_mode == PCTT_PRF_MODE_HPRF_HIGH_RATE && + p->psdu_data_rate < PCTT_PSDU_DATA_RATE_27M2) + return -EINVAL; + } + if ((p->rframe_config == PCTT_RFRAME_CONFIG_SP0) && + (p->number_of_sts_segments != PCTT_NUMBER_OF_STS_SEGMENTS_NONE)) + return -EINVAL; + if ((p->rframe_config != PCTT_RFRAME_CONFIG_SP0) && + (p->number_of_sts_segments == PCTT_NUMBER_OF_STS_SEGMENTS_NONE)) + return -EINVAL; + if ((p->rframe_config == PCTT_RFRAME_CONFIG_SP3) && + (p->data_payload_len)) + return -EINVAL; + + /* Set radio parameters. */ + switch (p->prf_mode) { + case PCTT_PRF_MODE_BPRF: + session->hrp_uwb_params.prf = MCPS802154_PRF_64; + break; + case PCTT_PRF_MODE_HPRF: + session->hrp_uwb_params.prf = MCPS802154_PRF_125; + break; + default: + session->hrp_uwb_params.prf = MCPS802154_PRF_250; + } + session->hrp_uwb_params.psr = + p->preamble_duration == PCTT_PREAMBLE_DURATION_64 ? + MCPS802154_PSR_64 : + MCPS802154_PSR_32; + session->hrp_uwb_params.sfd_selector = (enum mcps802154_sfd)(p->sfd_id); + session->hrp_uwb_params.phr_hi_rate = !!p->phr_data_rate; + session->hrp_uwb_params.data_rate = + pctt_rate_to_mcps_rate[p->psdu_data_rate]; + /* Update unique session context. */ session->first_access = true; + session->first_rx_synchronized = false; /* FIXME: Delete portid_set_once. * See: UWB-2057. */ session->portid_set_once = true; session->event_portid = info->snd_portid; /* Set parameters used by the PCTT vendor command. */ - session->setup_hw = (struct dw3000_vendor_cmd_pctt_setup_hw){ + session->setup_hw = (struct llhw_vendor_cmd_pctt_setup_hw){ .chan = p->channel_number, .rframe_config = p->rframe_config, .preamble_code_index = p->preamble_code_index, @@ -121,7 +185,7 @@ int pctt_session_start_test(struct pctt_local *local, enum pctt_id_attrs cmd_id, }; /* Update region context. */ memset(&local->results, 0, sizeof(local->results)); - local->frames_remaining_nb = session->test_params.num_packets; + local->frames_remaining_nb = session->params.num_packets; session->cmd_id = cmd_id; session->test_on_going = true; /* At the end, update the state. */ diff --git a/mac/pctt_session.h b/mac/pctt_session.h index 4297df5..40058e4 100644 --- a/mac/pctt_session.h +++ b/mac/pctt_session.h @@ -30,6 +30,8 @@ #include <net/pctt_region_params.h> #include <net/pctt_region_nl.h> +#define PCTT_PAYLOAD_MAX_LEN 4096 + struct pctt_session_params { enum pctt_device_role device_role; __le16 short_addr; @@ -40,17 +42,17 @@ struct pctt_session_params { int channel_number; int preamble_code_index; enum pctt_rframe_config rframe_config; - enum pctt_prf_mode prf_mode; enum pctt_preamble_duration preamble_duration; enum pctt_sfd_id sfd_id; enum pctt_number_of_sts_segments number_of_sts_segments; enum pctt_psdu_data_rate psdu_data_rate; enum pctt_mac_fcs_type mac_fcs_type; + enum pctt_prf_mode prf_mode; + enum pctt_phr_data_rate phr_data_rate; u8 tx_adaptive_payload_power; u32 sts_index; -}; - -struct pctt_test_params { + enum pctt_sts_length sts_length; + /* Test specific parameters */ u32 num_packets; int gap_duration_dtu; u32 t_start; @@ -60,6 +62,9 @@ struct pctt_test_params { u32 rmarker_tx_start; u32 rmarker_rx_start; u8 sts_index_auto_incr; + /* Data payload to put in TX test frame */ + u8 data_payload[PCTT_PAYLOAD_MAX_LEN]; + int data_payload_len; }; /** @@ -72,9 +77,10 @@ struct pctt_session { */ struct pctt_session_params params; /** - * @test_params: test parameters provided with the test id. + * @hrp_uwb_params: HRP UWB parameters, read only while the session is + * active. */ - struct pctt_test_params test_params; + struct mcps802154_hrp_uwb_params hrp_uwb_params; /** * @event_portid: Port identifier to use for notifications. */ @@ -90,17 +96,21 @@ struct pctt_session { */ bool first_access; /** + * @first_rx_synchronized: True after the first successful reception. + */ + bool first_rx_synchronized; + /** * @stop_request: True to not start an another access. */ bool stop_request; /** - * @next_timestamp_dtu: next date for PERIODIC_TX test. + * @next_timestamp_dtu: next date for next frame. */ u32 next_timestamp_dtu; /** * @setup_hw: setup hardware through a vendor command. */ - struct dw3000_vendor_cmd_pctt_setup_hw setup_hw; + struct llhw_vendor_cmd_pctt_setup_hw setup_hw; /** * @state: UWB session state. */ diff --git a/mac/pctt_trace.h b/mac/pctt_trace.h index a2fe1bf..3cba8e7 100644 --- a/mac/pctt_trace.h +++ b/mac/pctt_trace.h @@ -66,11 +66,13 @@ TRACE_DEFINE_ENUM(PCTT_RFRAME_CONFIG_SP3); #define pctt_prf_mode_name(name) \ { PCTT_PRF_MODE_##name, #name } -#define PCTT_PRF_MODE_SYMBOLS \ - pctt_prf_mode_name(BPRF), \ - pctt_prf_mode_name(HPRF) +#define PCTT_PRF_MODE_SYMBOLS \ + pctt_prf_mode_name(BPRF), \ + pctt_prf_mode_name(HPRF), \ + pctt_prf_mode_name(HPRF_HIGH_RATE) TRACE_DEFINE_ENUM(PCTT_PRF_MODE_BPRF); TRACE_DEFINE_ENUM(PCTT_PRF_MODE_HPRF); +TRACE_DEFINE_ENUM(PCTT_PRF_MODE_HPRF_HIGH_RATE); #define PCTT_PRF_MODE_ENTRY __field(enum pctt_prf_mode, prf_mode) #define PCTT_PRF_MODE_ASSIGN __entry->prf_mode = params->prf_mode #define PCTT_PRF_MODE_PR_FMT "prf_mode=%s" @@ -117,10 +119,14 @@ TRACE_DEFINE_ENUM(PCTT_SFD_ID_4); #define PCTT_NUMBER_OF_STS_SEGMENTS_SYMBOLS \ pctt_number_of_sts_segments_name(NONE), \ pctt_number_of_sts_segments_name(1_SEGMENT), \ - pctt_number_of_sts_segments_name(2_SEGMENTS) + pctt_number_of_sts_segments_name(2_SEGMENTS), \ + pctt_number_of_sts_segments_name(3_SEGMENTS), \ + pctt_number_of_sts_segments_name(4_SEGMENTS) TRACE_DEFINE_ENUM(PCTT_NUMBER_OF_STS_SEGMENTS_NONE); TRACE_DEFINE_ENUM(PCTT_NUMBER_OF_STS_SEGMENTS_1_SEGMENT); TRACE_DEFINE_ENUM(PCTT_NUMBER_OF_STS_SEGMENTS_2_SEGMENTS); +TRACE_DEFINE_ENUM(PCTT_NUMBER_OF_STS_SEGMENTS_3_SEGMENTS); +TRACE_DEFINE_ENUM(PCTT_NUMBER_OF_STS_SEGMENTS_4_SEGMENTS); #define PCTT_NUMBER_OF_STS_SEGMENTS_ENTRY \ __field(enum pctt_number_of_sts_segments, number_of_sts_segments) #define PCTT_NUMBER_OF_STS_SEGMENTS_ASSIGN \ @@ -149,6 +155,21 @@ TRACE_DEFINE_ENUM(PCTT_PSDU_DATA_RATE_31M2); #define PCTT_PSDU_DATA_RATE_PR_ARG \ __print_symbolic(__entry->psdu_data_rate, PCTT_PSDU_DATA_RATE_SYMBOLS) +#define pctt_phr_data_rate_name(name) \ + { PCTT_PHR_DATA_RATE##name, #name } +#define PCTT_PHR_DATA_RATE_SYMBOLS \ + pctt_phr_data_rate_name(850k), \ + pctt_phr_data_rate_name(6M81), \ +TRACE_DEFINE_ENUM(PCTT_PHR_DATA_RATE_850k); +TRACE_DEFINE_ENUM(PCTT_PHR_DATA_RATE_6M81); +#define PCTT_PHR_DATA_RATE_ENTRY \ + __field(enum pctt_phr_data_rate, phr_data_rate) +#define PCTT_PHR_DATA_RATE_ASSIGN \ + __entry->phr_data_rate = params->phr_data_rate +#define PCTT_PHR_DATA_RATE_PR_FMT "phr_data_rate=%s" +#define PCTT_PHR_DATA_RATE_PR_ARG \ + __print_symbolic(__entry->phr_data_rate, PCTT_PHR_DATA_RATE_SYMBOLS) + #define pctt_mac_fcs_type_name(name) \ { PCTT_MAC_FCS_TYPE_##name, #name } #define PCTT_MAC_FCS_TYPE_SYMBOLS \ @@ -356,6 +377,7 @@ TRACE_EVENT(region_pctt_report_per_rx, __field(u32, psdu_bit_error) __field(u32, sts_found) __field(u32, eof) + __field(u8, rssi) ), TP_fast_assign( PCTT_STATUS_RANGING_ASSIGN; @@ -372,20 +394,21 @@ TRACE_EVENT(region_pctt_report_per_rx, __entry->psdu_bit_error = per_rx->psdu_bit_error; __entry->sts_found = per_rx->sts_found; __entry->eof = per_rx->eof; + __entry->rssi = per_rx->rssi; ), TP_printk(PCTT_STATUS_RANGING_PR_FMT " " "attempts=%u acq_detect=%u acq_reject=%u " "rx_fail=%u sync_cir_ready=%u sfd_fail=%u " "sfd_found=%u phr_dec_error=%u phr_bit_error=%u " "psdu_dec_error=%u psdu_bit_error=%u sts_found=%u " - "eof=%u", + "eof=%u rssi=%u ", PCTT_STATUS_RANGING_PR_ARG, __entry->attempts, __entry->acq_detect, __entry->acq_reject, __entry->rx_fail, __entry->sync_cir_ready, __entry->sfd_fail, __entry->sfd_found, __entry->phr_dec_error, __entry->phr_bit_error, __entry->psdu_dec_error, __entry->psdu_bit_error, - __entry->sts_found, __entry->eof) + __entry->sts_found, __entry->eof, __entry->rssi) ); TRACE_EVENT(region_pctt_report_rx, @@ -400,6 +423,7 @@ TRACE_EVENT(region_pctt_report_rx, __field(s16, aoa_elevation) __field(u8, toa_gap) __field(u16, phr) + __field(u8, rssi) __field(u16, psdu_data_len) __dynamic_array(u8, psdu_data, rx->psdu_data_len) ), @@ -411,6 +435,7 @@ TRACE_EVENT(region_pctt_report_rx, __entry->aoa_elevation = rx->aoa_elevation; __entry->toa_gap = rx->toa_gap; __entry->phr = rx->phr; + __entry->rssi = rx->rssi; __entry->psdu_data_len = rx->psdu_data_len; memcpy(__get_dynamic_array(psdu_data), rx->psdu_data, __get_dynamic_array_len(psdu_data)); @@ -419,15 +444,28 @@ TRACE_EVENT(region_pctt_report_rx, TP_printk(PCTT_STATUS_RANGING_PR_FMT " " "rx_done_ts_int=%u rx_done_ts_frac=%u " "aoa_azimuth=%d aoa_elevation=%d toa_gap=%u " - "phr=%u psdu_data_len=%u psdu_data=%s", + "phr=%u rssi=%u psdu_data_len=%u psdu_data=%s", PCTT_STATUS_RANGING_PR_ARG, __entry->rx_done_ts_int, __entry->rx_done_ts_frac, __entry->aoa_azimuth, __entry->aoa_elevation, __entry->toa_gap, __entry->phr, - __entry->psdu_data_len, + __entry->rssi, __entry->psdu_data_len, __print_hex(__get_dynamic_array(psdu_data), __get_dynamic_array_len(psdu_data))) ); +TRACE_EVENT(region_pctt_report_loopback, + TP_PROTO(enum pctt_status_ranging status_ranging), + TP_ARGS(status_ranging), + TP_STRUCT__entry( + PCTT_STATUS_RANGING_ENTRY + ), + TP_fast_assign( + PCTT_STATUS_RANGING_ASSIGN; + ), + TP_printk(PCTT_STATUS_RANGING_PR_FMT, + PCTT_STATUS_RANGING_PR_ARG) +); + TRACE_EVENT(region_pctt_report_ss_twr, TP_PROTO(enum pctt_status_ranging status_ranging, const struct pctt_test_ss_twr_results *ss_twr), @@ -435,13 +473,27 @@ TRACE_EVENT(region_pctt_report_ss_twr, TP_STRUCT__entry( PCTT_STATUS_RANGING_ENTRY __field(u32, measurement_rctu) + __field(s16, pdoa_azimuth_deg_q7) + __field(s16, pdoa_elevation_deg_q7) + __field(u8, rssi) + __field(s16, aoa_azimuth_deg_q7) + __field(s16, aoa_elevation_deg_q7) ), TP_fast_assign( PCTT_STATUS_RANGING_ASSIGN; __entry->measurement_rctu = ss_twr->measurement_rctu; + __entry->pdoa_azimuth_deg_q7 = ss_twr->pdoa_azimuth_deg_q7; + __entry->pdoa_elevation_deg_q7 = ss_twr->pdoa_elevation_deg_q7; + __entry->rssi = ss_twr->rssi; + __entry->aoa_azimuth_deg_q7 = ss_twr->aoa_azimuth_deg_q7; + __entry->aoa_elevation_deg_q7 = ss_twr->aoa_elevation_deg_q7; ), - TP_printk(PCTT_STATUS_RANGING_PR_FMT " measurement_rctu=%u", - PCTT_STATUS_RANGING_PR_ARG, __entry->measurement_rctu) + TP_printk(PCTT_STATUS_RANGING_PR_FMT " measurement_rctu=%u " + "pdoa_azimuth_deg_q7=%u pdoa_elevation_deg_q7=%u " + "rssi=%u aoa_azimuth_deg_q7=%u aoa_elevation_deg_q7=%u", + PCTT_STATUS_RANGING_PR_ARG, __entry->measurement_rctu, + __entry->pdoa_azimuth_deg_q7, __entry->pdoa_elevation_deg_q7, + __entry->rssi, __entry->aoa_azimuth_deg_q7, __entry->aoa_elevation_deg_q7) ); TRACE_EVENT(region_pctt_report_nla_put_failure, diff --git a/mac/regions.c b/mac/regions.c index 98e3963..26ef8d8 100644 --- a/mac/regions.c +++ b/mac/regions.c @@ -30,6 +30,7 @@ #include <linux/netdevice.h> #include "mcps802154_i.h" +#include "trace.h" static LIST_HEAD(registered_regions); static DEFINE_MUTEX(registered_regions_lock); @@ -125,11 +126,11 @@ EXPORT_SYMBOL_GPL(mcps802154_region_close); void mcps802154_region_notify_stop(struct mcps802154_llhw *llhw, struct mcps802154_region *region) { - const struct mcps802154_region_ops *ops; + if (!region->ops->notify_stop) + return; - ops = region->ops; - if (ops->notify_stop) - ops->notify_stop(region); + trace_region_notify_stop(region); + region->ops->notify_stop(region); } EXPORT_SYMBOL_GPL(mcps802154_region_notify_stop); @@ -165,10 +166,16 @@ int mcps802154_region_get_demand(struct mcps802154_llhw *llhw, u32 next_timestamp_dtu, struct mcps802154_region_demand *demand) { + int r; + if (!region->ops->get_demand) return -EOPNOTSUPP; - return region->ops->get_demand(region, next_timestamp_dtu, demand); + trace_region_get_demand(region, next_timestamp_dtu); + r = region->ops->get_demand(region, next_timestamp_dtu, demand); + trace_region_get_demand_return(region, demand, r); + + return r; } EXPORT_SYMBOL_GPL(mcps802154_region_get_demand); @@ -206,3 +213,17 @@ void mcps802154_region_rx_skb(struct mcps802154_llhw *llhw, ieee802154_rx_irqsafe(local->hw, skb, lqi); } EXPORT_SYMBOL_GPL(mcps802154_region_rx_skb); + +int mcps802154_region_deferred(struct mcps802154_llhw *llhw, + struct mcps802154_region *region) +{ + struct mcps802154_local *local = llhw_to_local(llhw); + + if (local->fproc.deferred && local->fproc.deferred != region) + return -EINVAL; + + local->fproc.deferred = region; + + return 0; +} +EXPORT_SYMBOL_GPL(mcps802154_region_deferred); diff --git a/mac/schedule.c b/mac/schedule.c index 2d8fa8a..1ca0bfa 100644 --- a/mac/schedule.c +++ b/mac/schedule.c @@ -53,6 +53,8 @@ int mcps802154_schedule_update(struct mcps802154_local *local, sched->start_timestamp_dtu = next_timestamp_dtu; sched->duration_dtu = 0; expected_start_timestamp_dtu = next_timestamp_dtu; + } else if (sched->duration_dtu == 0) { + expected_start_timestamp_dtu = next_timestamp_dtu; } else { expected_start_timestamp_dtu = sched->start_timestamp_dtu + sched->duration_dtu; @@ -68,6 +70,9 @@ int mcps802154_schedule_update(struct mcps802154_local *local, scheduler = local->ca.scheduler; r = scheduler->ops->update_schedule(scheduler, su, next_timestamp_dtu); if (r) { + if (r != -ENOENT) + trace_update_schedule_error(local, su, + next_timestamp_dtu); mcps802154_schedule_clear(local); return r; } @@ -146,7 +151,8 @@ EXPORT_SYMBOL(mcps802154_schedule_recycle); int mcps802154_schedule_add_region( const struct mcps802154_schedule_update *schedule_update, - struct mcps802154_region *region, int start_dtu, int duration_dtu) + struct mcps802154_region *region, int start_dtu, int duration_dtu, + bool once) { struct mcps802154_schedule_update_local *sulocal = schedule_update_to_local(schedule_update); @@ -184,6 +190,7 @@ int mcps802154_schedule_add_region( sched_region->start_dtu = start_dtu; sched_region->duration_dtu = duration_dtu; sched_region->region = region; + sched_region->once = once; sched->regions = new_sched_regions; su->n_regions = sched->n_regions = sched->n_regions + 1; @@ -227,3 +234,19 @@ int mcps802154_schedule_get_regions(struct mcps802154_llhw *llhw, return local->ca.n_regions; } EXPORT_SYMBOL(mcps802154_schedule_get_regions); + +int mcps802154_schedule_get_next_demands( + struct mcps802154_llhw *llhw, const struct mcps802154_region *region, + u32 timestamp_dtu, int duration_dtu, int delta_dtu, + struct mcps802154_region_demand *demands) +{ + struct mcps802154_local *local = llhw_to_local(llhw); + struct mcps802154_scheduler *scheduler = local->ca.scheduler; + + if (!scheduler->ops->get_next_demands) + return -EOPNOTSUPP; + return scheduler->ops->get_next_demands(scheduler, region, + timestamp_dtu, duration_dtu, + delta_dtu, demands); +} +EXPORT_SYMBOL(mcps802154_schedule_get_next_demands); diff --git a/mac/schedule.h b/mac/schedule.h index 53bd51f..f17feae 100644 --- a/mac/schedule.h +++ b/mac/schedule.h @@ -45,6 +45,10 @@ struct mcps802154_schedule_region { * @duration_dtu: Region duration or 0 for endless region. */ int duration_dtu; + /** + * @once: Schedule the region once, ignoring the remaining region duration. + */ + bool once; }; /** diff --git a/mac/simple_ranging_region.c b/mac/simple_ranging_region.c deleted file mode 100644 index 20b5f6b..0000000 --- a/mac/simple_ranging_region.c +++ /dev/null @@ -1,941 +0,0 @@ -/* - * This file is part of the UWB stack for linux. - * - * Copyright (c) 2020-2021 Qorvo US, Inc. - * - * This software is provided under the GNU General Public License, version 2 - * (GPLv2), as well as under a Qorvo commercial license. - * - * You may choose to use this software under the terms of the GPLv2 License, - * version 2 ("GPLv2"), as published by the Free Software Foundation. - * You should have received a copy of the GPLv2 along with this program. If - * not, see <http://www.gnu.org/licenses/>. - * - * This program is distributed under the GPLv2 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 GPLv2 for more - * details. - * - * If you cannot meet the requirements of the GPLv2, you may not use this - * software for any purpose without first obtaining a commercial license from - * Qorvo. Please contact Qorvo to inquire about licensing terms. - */ - -#include <asm/unaligned.h> -#include <linux/errno.h> -#include <linux/ieee802154.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/limits.h> -#include <net/af_ieee802154.h> - -#include <net/mcps802154_schedule.h> -#include <net/mcps802154_frame.h> -#include <net/simple_ranging_region_nl.h> - -#include "nl.h" -#include "warn_return.h" - -#define TWR_SLOT_MS_TO_RCTU 67108864ull -#define TWR_SLOT_MS_MAX 64 -#define TWR_SLOT_DEFAULT_RCTU (16 * TWR_SLOT_MS_TO_RCTU) - -#define TWR_FUNCTION_CODE_POLL 0x40 -#define TWR_FUNCTION_CODE_RESP 0x41 -#define TWR_FUNCTION_CODE_FINAL 0x42 -#define TWR_FUNCTION_CODE_REPORT 0x43 - -enum twr_frames { - TWR_FRAME_POLL, - TWR_FRAME_RESP, - TWR_FRAME_FINAL, - TWR_FRAME_REPORT, - N_TWR_FRAMES, -}; - -struct simple_ranging_initiator { - u64 poll_tx_timestamp_rctu; - u64 final_tx_timestamp_rctu; - int tof_half_tag_rctu; - int local_pdoa_rad_q11; - s16 remote_pdoa_rad_q11; - int local_pdoa_elevation_rad_q11; - s16 remote_pdoa_elevation_rad_q11; -}; - -struct simple_ranging_responder { - u64 poll_rx_timestamp_rctu; - u64 resp_tx_timestamp_rctu; - s16 local_pdoa_rad_q11; - s16 local_pdoa_elevation_rad_q11; - int tof_x4_rctu; -}; - -struct simple_ranging_local { - struct mcps802154_scheduler scheduler; - struct mcps802154_region region_init; - struct mcps802154_region region_resp; - struct mcps802154_llhw *llhw; - struct mcps802154_access access; - struct mcps802154_access_frame frames[N_TWR_FRAMES]; - struct mcps802154_sts_params sts_params; - struct mcps802154_nl_ranging_request - requests[MCPS802154_NL_RANGING_REQUESTS_MAX]; - unsigned int n_requests; - unsigned int request_idx; - int frequency_hz; - struct mcps802154_nl_ranging_request current_request; - int slot_duration_dtu; - bool is_responder; - unsigned int tx_ant_set_id; - unsigned int rx_ant_set_id_azimuth; - unsigned int rx_ant_set_id_elevation; - bool is_same_rx_ant_set_id; - union { - struct simple_ranging_initiator initiator; - struct simple_ranging_responder responder; - }; -}; - -static inline struct simple_ranging_local * -scheduler_to_local(struct mcps802154_scheduler *scheduler) -{ - return container_of(scheduler, struct simple_ranging_local, scheduler); -} - -static inline struct simple_ranging_local * -access_to_local(struct mcps802154_access *access) -{ - return container_of(access, struct simple_ranging_local, access); -} - -static inline struct simple_ranging_local * -region_init_to_local(struct mcps802154_region *region_init) -{ - return container_of(region_init, struct simple_ranging_local, - region_init); -} - -static inline struct simple_ranging_local * -region_resp_to_local(struct mcps802154_region *region_resp) -{ - return container_of(region_resp, struct simple_ranging_local, - region_resp); -} - -/* Requests and reports. */ - -static void twr_requests_clear(struct simple_ranging_local *local) -{ - local->n_requests = 0; - local->request_idx = 0; - local->frequency_hz = 1; -} - -static void twr_request_start(struct simple_ranging_local *local) -{ - if (local->request_idx >= local->n_requests) - local->request_idx = 0; - local->current_request = local->requests[local->request_idx]; -} - -static void twr_report(struct simple_ranging_local *local, - const struct mcps802154_nl_ranging_report *report) -{ - struct mcps802154_nl_ranging_request *request = &local->current_request; - struct mcps802154_nl_ranging_report invalid = { - .tof_rctu = INT_MIN, - .local_pdoa_rad_q11 = INT_MIN, - .remote_pdoa_rad_q11 = INT_MIN, - .local_pdoa_elevation_rad_q11 = INT_MIN, - .remote_pdoa_elevation_rad_q11 = INT_MIN, - .is_same_rx_ant_set_id = local->is_same_rx_ant_set_id, - }; - int r; - - if (!report) - report = &invalid; - - r = mcps802154_nl_ranging_report(local->llhw, request->id, report); - - if (r == -ECONNREFUSED) - twr_requests_clear(local); - else - local->request_idx++; -} - -/* Frames. */ - -#define TWR_FRAME_HEADER_SIZE \ - (IEEE802154_FC_LEN + IEEE802154_SEQ_LEN + IEEE802154_PAN_ID_LEN + \ - IEEE802154_EXTENDED_ADDR_LEN * 2) -#define TWR_FRAME_POLL_SIZE 1 -#define TWR_FRAME_RESP_SIZE 3 -#define TWR_FRAME_FINAL_SIZE 5 -#define TWR_FRAME_REPORT_SIZE 7 -#define TWR_FRAME_MAX_SIZE (TWR_FRAME_HEADER_SIZE + TWR_FRAME_REPORT_SIZE) - -void twr_frame_header_fill_buf(u8 *buf, __le16 pan_id, __le64 dst, __le64 src) -{ - u16 fc = (IEEE802154_FC_TYPE_DATA | IEEE802154_FC_INTRA_PAN | - (IEEE802154_ADDR_LONG << IEEE802154_FC_DAMODE_SHIFT) | - (1 << IEEE802154_FC_VERSION_SHIFT) | - (IEEE802154_ADDR_LONG << IEEE802154_FC_SAMODE_SHIFT)); - u8 seq = 0; - size_t pos = 0; - - put_unaligned_le16(fc, buf + pos); - pos += IEEE802154_FC_LEN; - buf[pos] = seq; - pos += IEEE802154_SEQ_LEN; - memcpy(buf + pos, &pan_id, sizeof(pan_id)); - pos += IEEE802154_PAN_ID_LEN; - memcpy(buf + pos, &dst, sizeof(dst)); - pos += IEEE802154_EXTENDED_ADDR_LEN; - memcpy(buf + pos, &src, sizeof(src)); -} - -void twr_frame_header_put(struct sk_buff *skb, __le16 pan_id, __le64 dst, - __le64 src) -{ - twr_frame_header_fill_buf(skb_put(skb, TWR_FRAME_HEADER_SIZE), pan_id, - dst, src); -} - -bool twr_frame_header_check(struct sk_buff *skb, __le16 pan_id, __le64 dst, - __le64 src) -{ - u8 buf[TWR_FRAME_HEADER_SIZE]; - - if (skb->len < TWR_FRAME_HEADER_SIZE) - return false; - twr_frame_header_fill_buf(buf, pan_id, dst, src); - if (memcmp(skb->data, buf, TWR_FRAME_HEADER_SIZE) != 0) - return false; - return true; -} - -bool twr_frame_header_check_no_src(struct sk_buff *skb, __le16 pan_id, - __le64 dst) -{ - /* Ignore source. */ - const int check_size = - TWR_FRAME_HEADER_SIZE - IEEE802154_EXTENDED_ADDR_LEN; - u8 buf[TWR_FRAME_HEADER_SIZE]; - - if (skb->len < TWR_FRAME_HEADER_SIZE) - return false; - twr_frame_header_fill_buf(buf, pan_id, dst, 0); - if (memcmp(skb->data, buf, check_size) != 0) - return false; - return true; -} - -void twr_frame_poll_put(struct sk_buff *skb) -{ - u8 function_code = TWR_FUNCTION_CODE_POLL; - - skb_put_u8(skb, function_code); -} - -bool twr_frame_poll_check(struct sk_buff *skb) -{ - u8 function_code; - - if (skb->len < TWR_FRAME_POLL_SIZE) - return false; - function_code = skb->data[0]; - if (function_code != TWR_FUNCTION_CODE_POLL) - return false; - return true; -} - -void twr_frame_resp_put(struct sk_buff *skb, s16 local_pdoa_rad_q11) -{ - u8 function_code = TWR_FUNCTION_CODE_RESP; - - skb_put_u8(skb, function_code); - put_unaligned_le16(local_pdoa_rad_q11, skb_put(skb, 2)); -} - -bool twr_frame_resp_check(struct sk_buff *skb, s16 *remote_pdoa_rad_q11) -{ - u8 function_code; - - if (skb->len < TWR_FRAME_RESP_SIZE) - return false; - function_code = skb->data[0]; - if (function_code != TWR_FUNCTION_CODE_RESP) - return false; - *remote_pdoa_rad_q11 = get_unaligned_le16(skb->data + 1); - return true; -} - -void twr_frame_final_put(struct sk_buff *skb, s32 tof_half_tag_rctu) -{ - u8 function_code = TWR_FUNCTION_CODE_FINAL; - - skb_put_u8(skb, function_code); - put_unaligned_le32(tof_half_tag_rctu, skb_put(skb, 4)); -} - -bool twr_frame_final_check(struct sk_buff *skb, s32 *tof_half_tag_rctu) -{ - u8 function_code; - - if (skb->len < TWR_FRAME_FINAL_SIZE) - return false; - function_code = skb->data[0]; - if (function_code != TWR_FUNCTION_CODE_FINAL) - return false; - *tof_half_tag_rctu = get_unaligned_le32(skb->data + 1); - return true; -} - -void twr_frame_report_put(struct sk_buff *skb, s32 tof_x4_rctu, - s16 local_pdoa_rad_q11) -{ - u8 function_code = TWR_FUNCTION_CODE_REPORT; - - skb_put_u8(skb, function_code); - put_unaligned_le32(tof_x4_rctu, skb_put(skb, 4)); - put_unaligned_le16(local_pdoa_rad_q11, skb_put(skb, 2)); -} - -bool twr_frame_report_check(struct sk_buff *skb, s32 *tof_x4_rctu, - s16 *remote_pdoa_rad_q11) -{ - u8 function_code; - - if (skb->len < TWR_FRAME_REPORT_SIZE) - return false; - function_code = skb->data[0]; - if (function_code != TWR_FUNCTION_CODE_REPORT) - return false; - *tof_x4_rctu = get_unaligned_le32(skb->data + 1); - *remote_pdoa_rad_q11 = get_unaligned_le16(skb->data + 5); - return true; -} - -/* Access responder. */ - -static void twr_responder_rx_frame(struct mcps802154_access *access, - int frame_idx, struct sk_buff *skb, - const struct mcps802154_rx_frame_info *info, - enum mcps802154_rx_error_type error) -{ - struct simple_ranging_local *local = access_to_local(access); - struct mcps802154_nl_ranging_request *request = &local->current_request; - - if (!skb) - goto fail; - - if (frame_idx == TWR_FRAME_POLL) { - u32 resp_tx_dtu; - - if (!twr_frame_header_check_no_src( - skb, mcps802154_get_pan_id(local->llhw), - mcps802154_get_extended_addr(local->llhw))) - goto fail_free_skb; - request->peer_extended_addr = - get_unaligned_le64(skb->data + TWR_FRAME_HEADER_SIZE - - IEEE802154_EXTENDED_ADDR_LEN); - skb_pull(skb, TWR_FRAME_HEADER_SIZE); - - if (!twr_frame_poll_check(skb)) - goto fail_free_skb; - if (!(info->flags & MCPS802154_RX_FRAME_INFO_TIMESTAMP_DTU)) - goto fail_free_skb; - if (!(info->flags & MCPS802154_RX_FRAME_INFO_TIMESTAMP_RCTU)) - goto fail_free_skb; - if (!(info->flags & MCPS802154_RX_FRAME_INFO_RANGING_PDOA)) - local->responder.local_pdoa_rad_q11 = S16_MIN; - else - local->responder.local_pdoa_rad_q11 = - info->ranging_pdoa_rad_q11; - local->responder.poll_rx_timestamp_rctu = info->timestamp_rctu; - resp_tx_dtu = info->timestamp_dtu + local->slot_duration_dtu; - local->responder.resp_tx_timestamp_rctu = - mcps802154_tx_timestamp_dtu_to_rmarker_rctu( - local->llhw, resp_tx_dtu, local->tx_ant_set_id); - /* Set the timings for the next frames. */ - access->frames[TWR_FRAME_RESP].tx_frame_info.timestamp_dtu = - resp_tx_dtu; - access->frames[TWR_FRAME_FINAL].rx.info.timestamp_dtu = - resp_tx_dtu + local->slot_duration_dtu; - access->frames[TWR_FRAME_REPORT].tx_frame_info.timestamp_dtu = - resp_tx_dtu + 2 * local->slot_duration_dtu; - } else { - s32 tof_half_rctu; - u64 final_rx_timestamp_rctu; - - WARN_ON(frame_idx != TWR_FRAME_FINAL); - if (!twr_frame_header_check( - skb, mcps802154_get_pan_id(local->llhw), - mcps802154_get_extended_addr(local->llhw), - request->peer_extended_addr)) - goto fail_free_skb; - skb_pull(skb, TWR_FRAME_HEADER_SIZE); - if (!twr_frame_final_check(skb, &tof_half_rctu)) - goto fail_free_skb; - if (!(info->flags & MCPS802154_RX_FRAME_INFO_TIMESTAMP_RCTU)) - goto fail_free_skb; - if (!(info->flags & MCPS802154_RX_FRAME_INFO_RANGING_PDOA)) - local->responder.local_pdoa_elevation_rad_q11 = S16_MIN; - else - local->responder.local_pdoa_elevation_rad_q11 = - info->ranging_pdoa_rad_q11; - final_rx_timestamp_rctu = info->timestamp_rctu; - local->responder.tof_x4_rctu = - tof_half_rctu - - mcps802154_difference_timestamp_rctu( - local->llhw, - local->responder.resp_tx_timestamp_rctu, - local->responder.poll_rx_timestamp_rctu) + - mcps802154_difference_timestamp_rctu( - local->llhw, final_rx_timestamp_rctu, - local->responder.resp_tx_timestamp_rctu); - } - - kfree_skb(skb); - return; -fail_free_skb: - kfree_skb(skb); -fail: - access->n_frames = frame_idx + 1; -} - -static struct sk_buff * -twr_responder_tx_get_frame(struct mcps802154_access *access, int frame_idx) -{ - struct simple_ranging_local *local = access_to_local(access); - struct mcps802154_nl_ranging_request *request = &local->current_request; - struct sk_buff *skb = mcps802154_frame_alloc( - local->llhw, TWR_FRAME_MAX_SIZE, GFP_KERNEL); - - twr_frame_header_put(skb, mcps802154_get_pan_id(local->llhw), - request->peer_extended_addr, - mcps802154_get_extended_addr(local->llhw)); - if (frame_idx == TWR_FRAME_RESP) { - twr_frame_resp_put(skb, local->responder.local_pdoa_rad_q11); - } else { - WARN_ON(frame_idx != TWR_FRAME_REPORT); - twr_frame_report_put( - skb, local->responder.tof_x4_rctu, - local->responder.local_pdoa_elevation_rad_q11); - } - return skb; -} - -static void -twr_responder_tx_return(struct mcps802154_access *access, int frame_idx, - struct sk_buff *skb, - enum mcps802154_access_tx_return_reason reason) -{ - kfree_skb(skb); -} - -struct mcps802154_access_ops twr_responder_access_ops = { - .rx_frame = twr_responder_rx_frame, - .tx_get_frame = twr_responder_tx_get_frame, - .tx_return = twr_responder_tx_return, -}; - -/* Access initiator. */ - -static void twr_rx_frame(struct mcps802154_access *access, int frame_idx, - struct sk_buff *skb, - const struct mcps802154_rx_frame_info *info, - enum mcps802154_rx_error_type error) -{ - struct simple_ranging_local *local = access_to_local(access); - struct mcps802154_nl_ranging_request *request = &local->current_request; - u64 resp_rx_timestamp_rctu; - struct mcps802154_nl_ranging_report report; - - if (!skb) - goto fail; - - if (!twr_frame_header_check(skb, mcps802154_get_pan_id(local->llhw), - mcps802154_get_extended_addr(local->llhw), - request->peer_extended_addr)) - goto fail_free_skb; - - skb_pull(skb, TWR_FRAME_HEADER_SIZE); - - if (frame_idx == TWR_FRAME_RESP) { - if (!twr_frame_resp_check( - skb, &local->initiator.remote_pdoa_rad_q11)) - goto fail_free_skb; - if (!(info->flags & MCPS802154_RX_FRAME_INFO_TIMESTAMP_RCTU)) - goto fail_free_skb; - if (!(info->flags & MCPS802154_RX_FRAME_INFO_RANGING_PDOA)) - local->initiator.local_pdoa_rad_q11 = INT_MIN; - else - local->initiator.local_pdoa_rad_q11 = - info->ranging_pdoa_rad_q11; - - resp_rx_timestamp_rctu = info->timestamp_rctu; - local->initiator.tof_half_tag_rctu = - mcps802154_difference_timestamp_rctu( - local->llhw, resp_rx_timestamp_rctu, - local->initiator.poll_tx_timestamp_rctu) - - mcps802154_difference_timestamp_rctu( - local->llhw, - local->initiator.final_tx_timestamp_rctu, - resp_rx_timestamp_rctu); - } else { - s32 report_tof_x4_rctu; - - WARN_ON(frame_idx != TWR_FRAME_REPORT); - if (!twr_frame_report_check( - skb, &report_tof_x4_rctu, - &local->initiator.remote_pdoa_elevation_rad_q11)) - goto fail_free_skb; - - if (!(info->flags & MCPS802154_RX_FRAME_INFO_RANGING_PDOA)) - local->initiator.local_pdoa_elevation_rad_q11 = INT_MIN; - else - local->initiator.local_pdoa_elevation_rad_q11 = - info->ranging_pdoa_rad_q11; - - report.tof_rctu = report_tof_x4_rctu / 4; - report.local_pdoa_rad_q11 = local->initiator.local_pdoa_rad_q11; - report.remote_pdoa_rad_q11 = - local->initiator.remote_pdoa_rad_q11; - report.local_pdoa_elevation_rad_q11 = - local->initiator.local_pdoa_elevation_rad_q11; - report.remote_pdoa_elevation_rad_q11 = - local->initiator.remote_pdoa_elevation_rad_q11; - report.is_same_rx_ant_set_id = local->is_same_rx_ant_set_id; - - twr_report(local, &report); - } - - kfree_skb(skb); - return; -fail_free_skb: - kfree_skb(skb); -fail: - twr_report(local, NULL); - access->n_frames = frame_idx + 1; -} - -static struct sk_buff *twr_tx_get_frame(struct mcps802154_access *access, - int frame_idx) -{ - struct simple_ranging_local *local = access_to_local(access); - struct mcps802154_nl_ranging_request *request = &local->current_request; - struct sk_buff *skb = mcps802154_frame_alloc( - local->llhw, TWR_FRAME_MAX_SIZE, GFP_KERNEL); - - twr_frame_header_put(skb, mcps802154_get_pan_id(local->llhw), - request->peer_extended_addr, - mcps802154_get_extended_addr(local->llhw)); - if (frame_idx == TWR_FRAME_POLL) { - twr_frame_poll_put(skb); - } else { - WARN_ON(frame_idx != TWR_FRAME_FINAL); - twr_frame_final_put(skb, local->initiator.tof_half_tag_rctu); - } - return skb; -} - -static void twr_tx_return(struct mcps802154_access *access, int frame_idx, - struct sk_buff *skb, - enum mcps802154_access_tx_return_reason reason) -{ - kfree_skb(skb); -} - -struct mcps802154_access_ops twr_access_ops = { - .rx_frame = twr_rx_frame, - .tx_get_frame = twr_tx_get_frame, - .tx_return = twr_tx_return, -}; - -/* Region responder. */ - -static struct mcps802154_access * -twr_responder_get_access(struct mcps802154_region *region, - u32 next_timestamp_dtu, int next_in_region_dtu, - int region_duration_dtu) -{ - struct simple_ranging_local *local = region_resp_to_local(region); - struct mcps802154_access *access = &local->access; - u32 start_dtu = next_timestamp_dtu; - - access->method = MCPS802154_ACCESS_METHOD_MULTI; - access->ops = &twr_responder_access_ops; - access->n_frames = ARRAY_SIZE(local->frames); - access->frames = local->frames; - - access->frames[TWR_FRAME_POLL] = (struct mcps802154_access_frame){ - .is_tx = false, - .rx = { - .info = { - .timestamp_dtu = start_dtu, - .timeout_dtu = -1, - .flags = MCPS802154_RX_INFO_TIMESTAMP_DTU | - MCPS802154_RX_INFO_RANGING | - MCPS802154_RX_INFO_KEEP_RANGING_CLOCK | - MCPS802154_RX_INFO_RANGING_PDOA | - MCPS802154_RX_INFO_SP1 | - MCPS802154_RX_INFO_RANGING_ROUND, - .ant_set_id = local->rx_ant_set_id_azimuth, - }, - .frame_info_flags_request = - MCPS802154_RX_FRAME_INFO_TIMESTAMP_DTU | - MCPS802154_RX_FRAME_INFO_TIMESTAMP_RCTU | - MCPS802154_RX_FRAME_INFO_RANGING_PDOA, - }, - .sts_params = &local->sts_params, - }; - - access->frames[TWR_FRAME_RESP] = (struct mcps802154_access_frame){ - .is_tx = true, - .tx_frame_info = { - .flags = MCPS802154_TX_FRAME_TIMESTAMP_DTU | - MCPS802154_TX_FRAME_RANGING | - MCPS802154_TX_FRAME_KEEP_RANGING_CLOCK | - MCPS802154_TX_FRAME_SP1, - .ant_set_id = local->tx_ant_set_id, - }, - }; - - access->frames[TWR_FRAME_FINAL] = (struct mcps802154_access_frame){ - .is_tx = false, - .rx = { - .info = { - .flags = MCPS802154_RX_INFO_TIMESTAMP_DTU | - MCPS802154_RX_INFO_RANGING | - MCPS802154_RX_INFO_RANGING_PDOA | - MCPS802154_RX_INFO_SP1, - .ant_set_id = local->rx_ant_set_id_elevation, - }, - .frame_info_flags_request = - MCPS802154_RX_FRAME_INFO_TIMESTAMP_RCTU | - MCPS802154_RX_FRAME_INFO_RANGING_PDOA, - }, - }; - - access->frames[TWR_FRAME_REPORT] = (struct mcps802154_access_frame){ - .is_tx = true, - .tx_frame_info = { - .flags = MCPS802154_TX_FRAME_TIMESTAMP_DTU | - MCPS802154_TX_FRAME_RANGING | - MCPS802154_TX_FRAME_SP1, - .ant_set_id = local->tx_ant_set_id, - }, - }; - - return access; -} - -static const struct mcps802154_region_ops - simple_ranging_twr_responder_region_ops = { - .name = "twr_responder", - .get_access = twr_responder_get_access, - }; - -/* Region initiator. */ - -static struct mcps802154_access * -twr_get_access(struct mcps802154_region *region, u32 next_timestamp_dtu, - int next_in_region_dtu, int region_duration_dtu) -{ - struct simple_ranging_local *local = region_init_to_local(region); - struct mcps802154_access *access = &local->access; - const int slots_dtu = local->slot_duration_dtu * N_TWR_FRAMES; - - /* Do nothing if nothing to do. */ - if (local->n_requests == 0) - return NULL; - - /* Only start a ranging request if we have enough time to end it. */ - if (next_in_region_dtu + slots_dtu > region_duration_dtu) - return NULL; - - twr_request_start(local); - access->method = MCPS802154_ACCESS_METHOD_MULTI; - access->ops = &twr_access_ops; - access->n_frames = ARRAY_SIZE(local->frames); - access->frames = local->frames; - - access->frames[TWR_FRAME_POLL] = (struct mcps802154_access_frame){ - .is_tx = true, - .tx_frame_info = { - .timestamp_dtu = next_timestamp_dtu, - .flags = MCPS802154_TX_FRAME_TIMESTAMP_DTU | - MCPS802154_TX_FRAME_RANGING | - MCPS802154_TX_FRAME_KEEP_RANGING_CLOCK | - MCPS802154_TX_FRAME_SP1 | - MCPS802154_TX_FRAME_RANGING_ROUND, - .ant_set_id = local->tx_ant_set_id, - }, - .sts_params = &local->sts_params, - }; - local->initiator.poll_tx_timestamp_rctu = - mcps802154_tx_timestamp_dtu_to_rmarker_rctu( - local->llhw, next_timestamp_dtu, local->tx_ant_set_id); - - access->frames[TWR_FRAME_RESP] = (struct mcps802154_access_frame){ - .is_tx = false, - .rx = { - .info = { - .timestamp_dtu = next_timestamp_dtu + - local->slot_duration_dtu, - .flags = MCPS802154_RX_INFO_TIMESTAMP_DTU | - MCPS802154_RX_INFO_RANGING | - MCPS802154_RX_INFO_KEEP_RANGING_CLOCK | - MCPS802154_RX_INFO_RANGING_PDOA | - MCPS802154_RX_INFO_SP1, - .ant_set_id = local->rx_ant_set_id_azimuth, - }, - .frame_info_flags_request = - MCPS802154_RX_FRAME_INFO_TIMESTAMP_RCTU | - MCPS802154_RX_FRAME_INFO_RANGING_PDOA, - }, - }; - - access->frames[TWR_FRAME_FINAL] = (struct mcps802154_access_frame){ - .is_tx = true, - .tx_frame_info = { - .timestamp_dtu = next_timestamp_dtu + - 2 * local->slot_duration_dtu, - .flags = MCPS802154_TX_FRAME_TIMESTAMP_DTU | - MCPS802154_TX_FRAME_RANGING | - MCPS802154_TX_FRAME_SP1, - .ant_set_id = local->tx_ant_set_id, - }, - }; - local->initiator.final_tx_timestamp_rctu = - mcps802154_tx_timestamp_dtu_to_rmarker_rctu( - local->llhw, - next_timestamp_dtu + 2 * local->slot_duration_dtu, - local->tx_ant_set_id); - - access->frames[TWR_FRAME_REPORT] = (struct mcps802154_access_frame){ - .is_tx = false, - .rx = { - .info = { - .timestamp_dtu = next_timestamp_dtu + - 3 * local->slot_duration_dtu, - .flags = MCPS802154_RX_INFO_TIMESTAMP_DTU | - MCPS802154_RX_INFO_RANGING | - MCPS802154_RX_INFO_RANGING_PDOA | - MCPS802154_RX_INFO_SP1, - .ant_set_id = local->rx_ant_set_id_elevation, - }, - .frame_info_flags_request = - MCPS802154_RX_FRAME_INFO_RANGING_PDOA, - }, - }; - - return access; -} - -static const struct mcps802154_region_ops simple_ranging_twr_region_ops = { - .name = "twr_initiator", - .get_access = twr_get_access, -}; - -/* Scheduler. */ - -static struct mcps802154_scheduler * -simple_ranging_scheduler_open(struct mcps802154_llhw *llhw) -{ - struct simple_ranging_local *local; - - local = kzalloc(sizeof(*local), GFP_KERNEL); - if (!local) - return NULL; - local->region_resp.ops = &simple_ranging_twr_responder_region_ops; - local->region_init.ops = &simple_ranging_twr_region_ops; - local->llhw = llhw; - local->sts_params.n_segs = 1; - local->sts_params.seg_len = 256; - local->slot_duration_dtu = TWR_SLOT_DEFAULT_RCTU / llhw->dtu_rctu; - local->is_responder = false; - local->tx_ant_set_id = TX_ANT_SET_ID_DEFAULT; - local->rx_ant_set_id_azimuth = RX_ANT_SET_ID_DEFAULT; - local->rx_ant_set_id_elevation = RX_ANT_SET_ID_DEFAULT; - local->is_same_rx_ant_set_id = true; - - twr_requests_clear(local); - return &local->scheduler; -} - -static void -simple_ranging_scheduler_close(struct mcps802154_scheduler *scheduler) -{ - struct simple_ranging_local *local = scheduler_to_local(scheduler); - - kfree(local); -} - -static int simple_ranging_scheduler_update_schedule( - struct mcps802154_scheduler *scheduler, - const struct mcps802154_schedule_update *schedule_update, - u32 next_timestamp_dtu) -{ - struct simple_ranging_local *local = scheduler_to_local(scheduler); - const int slot_dtu = local->slot_duration_dtu; - int twr_slots = local->n_requests * N_TWR_FRAMES; - int r; - - if (schedule_update->n_regions) { - int schedule_duration_slots = local->llhw->dtu_freq_hz / - slot_dtu / local->frequency_hz; - /* This treatment is done only for initiator. - * Responder region never enters here. As it is an infinite - * region. */ - WARN_ON(local->is_responder); - if (schedule_duration_slots < twr_slots) - schedule_duration_slots = twr_slots; - - r = mcps802154_schedule_set_start( - schedule_update, - schedule_update->expected_start_timestamp_dtu + - (schedule_duration_slots - twr_slots) * - slot_dtu); - WARN_RETURN(r); - } - - r = mcps802154_schedule_recycle(schedule_update, 0, - MCPS802154_DURATION_NO_CHANGE); - WARN_RETURN(r); - - if (local->is_responder) { - r = mcps802154_schedule_add_region(schedule_update, - &local->region_resp, 0, 0); - } else { - r = mcps802154_schedule_add_region(schedule_update, - &local->region_init, 0, - twr_slots * slot_dtu); - } - return r; -} - -static int simple_ranging_scheduler_ranging_setup( - struct mcps802154_scheduler *scheduler, - const struct mcps802154_nl_ranging_request *requests, - unsigned int n_requests) -{ - struct simple_ranging_local *local = scheduler_to_local(scheduler); - bool need_invalidate = local->n_requests == 0; - int max_frequency_hz = 1; - int i; - - if (local->is_responder) - return -EOPNOTSUPP; - - if (n_requests > MCPS802154_NL_RANGING_REQUESTS_MAX) - return -EINVAL; - - for (i = 0; i < n_requests; i++) { - if (requests[i].remote_peer_extended_addr) - return -EOPNOTSUPP; - local->requests[i] = requests[i]; - if (requests[i].frequency_hz > max_frequency_hz) - max_frequency_hz = requests[i].frequency_hz; - } - local->n_requests = n_requests; - local->frequency_hz = max_frequency_hz; - - if (need_invalidate) - mcps802154_schedule_invalidate(local->llhw); - - return 0; -} - -static int -simple_ranging_scheduler_set_parameters(struct mcps802154_scheduler *scheduler, - const struct nlattr *params_attr, - struct netlink_ext_ack *extack) -{ - struct simple_ranging_local *local = scheduler_to_local(scheduler); - struct nlattr *attrs[SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_MAX + 1]; - int r; - static const struct nla_policy nla_policy[SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_MAX + - 1] = { - [SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_SLOT_DURATION_MS] = { .type = NLA_U32 }, - [SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_NODE_TYPE] = { .type = NLA_U32, - .validation_type = - NLA_VALIDATE_MAX, - .max = 1, }, - [SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_TX_ANTENNA] = { .type = NLA_U8 }, - [SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_RX_ANTENNA_PAIR_AZIMUTH] = { .type = NLA_U8 }, - [SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_RX_ANTENNA_PAIR_ELEVATION] = { .type = NLA_U8 }, - }; - - r = nla_parse_nested(attrs, - SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_MAX, - params_attr, nla_policy, extack); - if (r) - return r; - - if (attrs[SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_SLOT_DURATION_MS]) { - int slot_duration_ms = nla_get_u32( - attrs[SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_SLOT_DURATION_MS]); - - if (!slot_duration_ms || - (slot_duration_ms & (slot_duration_ms - 1)) || - slot_duration_ms > TWR_SLOT_MS_MAX) - return -EINVAL; - - local->slot_duration_dtu = slot_duration_ms * - TWR_SLOT_MS_TO_RCTU / - local->llhw->dtu_rctu; - } - if (attrs[SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_NODE_TYPE]) { - u32 type = nla_get_u32( - attrs[SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_NODE_TYPE]); - - local->is_responder = type == 1 ? true : false; - mcps802154_schedule_invalidate(local->llhw); - } - - if (attrs[SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_TX_ANTENNA]) { - u8 id = nla_get_u8( - attrs[SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_TX_ANTENNA]); - local->tx_ant_set_id = id; - } - if (attrs[SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_RX_ANTENNA_PAIR_AZIMUTH]) { - u8 id = nla_get_u8( - attrs[SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_RX_ANTENNA_PAIR_AZIMUTH]); - local->rx_ant_set_id_azimuth = id; - } - if (attrs[SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_RX_ANTENNA_PAIR_ELEVATION]) { - u8 id = nla_get_u8( - attrs[SIMPLE_RANGING_REGION_SET_PARAMETERS_ATTR_RX_ANTENNA_PAIR_ELEVATION]); - local->rx_ant_set_id_elevation = id; - } - - local->is_same_rx_ant_set_id = local->rx_ant_set_id_azimuth == - local->rx_ant_set_id_elevation; - - return 0; -} - -static struct mcps802154_scheduler_ops simple_ranging_scheduler_ops = { - .owner = THIS_MODULE, - .name = "simple-ranging", - .open = simple_ranging_scheduler_open, - .close = simple_ranging_scheduler_close, - .update_schedule = simple_ranging_scheduler_update_schedule, - .ranging_setup = simple_ranging_scheduler_ranging_setup, - .set_parameters = simple_ranging_scheduler_set_parameters, -}; - -int __init simple_ranging_region_init(void) -{ - int r = mcps802154_scheduler_register(&simple_ranging_scheduler_ops); - /* TODO: register regions when they can be used from another scheduler. */ - return r; -} - -void __exit simple_ranging_region_exit(void) -{ - mcps802154_scheduler_unregister(&simple_ranging_scheduler_ops); -} diff --git a/mac/trace.h b/mac/trace.h index 7ba4d88..a8e2011 100644 --- a/mac/trace.h +++ b/mac/trace.h @@ -29,6 +29,7 @@ #include <linux/tracepoint.h> #include "mcps802154_i.h" +#include "idle_region.h" /* clang-format off */ @@ -37,72 +38,78 @@ #define LOCAL_PR_FMT "hw%d" #define LOCAL_PR_ARG __entry->hw_idx -#define mcps802154_tx_frame_name(name) \ +#define mcps802154_tx_frame_config_name(name) \ { \ - MCPS802154_TX_FRAME_##name, #name \ + MCPS802154_TX_FRAME_CONFIG_##name, #name \ } -#define MCPS802154_TX_FRAME_NAMES \ - mcps802154_tx_frame_name(TIMESTAMP_DTU), \ - mcps802154_tx_frame_name(CCA), \ - mcps802154_tx_frame_name(RANGING), \ - mcps802154_tx_frame_name(KEEP_RANGING_CLOCK), \ - mcps802154_tx_frame_name(RANGING_PDOA), \ - mcps802154_tx_frame_name(SP1), \ - mcps802154_tx_frame_name(SP2), \ - mcps802154_tx_frame_name(SP3), \ - mcps802154_tx_frame_name(STS_MODE_MASK), \ - mcps802154_tx_frame_name(RANGING_ROUND) -TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_TIMESTAMP_DTU); -TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_CCA); -TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_RANGING); -TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_KEEP_RANGING_CLOCK); -TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_RANGING_PDOA); -TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_SP1); -TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_SP2); -TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_SP3); -TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_STS_MODE_MASK); -TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_RANGING_ROUND); - -#define mcps802154_rx_info_name(name) \ +#define MCPS802154_TX_FRAME_CONFIG_NAMES \ + mcps802154_tx_frame_config_name(TIMESTAMP_DTU), \ + mcps802154_tx_frame_config_name(CCA), \ + mcps802154_tx_frame_config_name(RANGING), \ + mcps802154_tx_frame_config_name(KEEP_RANGING_CLOCK), \ + mcps802154_tx_frame_config_name(RANGING_PDOA), \ + mcps802154_tx_frame_config_name(SP1), \ + mcps802154_tx_frame_config_name(SP2), \ + mcps802154_tx_frame_config_name(SP3), \ + mcps802154_tx_frame_config_name(STS_MODE_MASK), \ + mcps802154_tx_frame_config_name(RANGING_ROUND) +TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_CONFIG_TIMESTAMP_DTU); +TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_CONFIG_CCA); +TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_CONFIG_RANGING); +TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_CONFIG_KEEP_RANGING_CLOCK); +TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_CONFIG_RANGING_PDOA); +TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_CONFIG_SP1); +TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_CONFIG_SP2); +TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_CONFIG_SP3); +TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_CONFIG_STS_MODE_MASK); +TRACE_DEFINE_ENUM(MCPS802154_TX_FRAME_CONFIG_RANGING_ROUND); + +#define mcps802154_rx_frame_config_name(name) \ { \ - MCPS802154_RX_INFO_##name, #name \ + MCPS802154_RX_FRAME_CONFIG_##name, #name \ } -#define MCPS802154_RX_INFO_NAMES \ - mcps802154_rx_info_name(TIMESTAMP_DTU), \ - mcps802154_rx_info_name(AACK), \ - mcps802154_rx_info_name(RANGING), \ - mcps802154_rx_info_name(KEEP_RANGING_CLOCK), \ - mcps802154_rx_info_name(RANGING_PDOA), \ - mcps802154_rx_info_name(SP1), \ - mcps802154_rx_info_name(SP2), \ - mcps802154_rx_info_name(SP3), \ - mcps802154_rx_info_name(STS_MODE_MASK), \ - mcps802154_rx_info_name(RANGING_ROUND) -TRACE_DEFINE_ENUM(MCPS802154_RX_INFO_TIMESTAMP_DTU); -TRACE_DEFINE_ENUM(MCPS802154_RX_INFO_AACK); -TRACE_DEFINE_ENUM(MCPS802154_RX_INFO_RANGING); -TRACE_DEFINE_ENUM(MCPS802154_RX_INFO_KEEP_RANGING_CLOCK); -TRACE_DEFINE_ENUM(MCPS802154_RX_INFO_RANGING_PDOA); -TRACE_DEFINE_ENUM(MCPS802154_RX_INFO_SP1); -TRACE_DEFINE_ENUM(MCPS802154_RX_INFO_SP2); -TRACE_DEFINE_ENUM(MCPS802154_RX_INFO_SP3); -TRACE_DEFINE_ENUM(MCPS802154_RX_INFO_STS_MODE_MASK); -TRACE_DEFINE_ENUM(MCPS802154_RX_INFO_RANGING_ROUND); +#define MCPS802154_RX_FRAME_CONFIG_NAMES \ + mcps802154_rx_frame_config_name(TIMESTAMP_DTU), \ + mcps802154_rx_frame_config_name(AACK), \ + mcps802154_rx_frame_config_name(RANGING), \ + mcps802154_rx_frame_config_name(KEEP_RANGING_CLOCK), \ + mcps802154_rx_frame_config_name(RANGING_PDOA), \ + mcps802154_rx_frame_config_name(SP1), \ + mcps802154_rx_frame_config_name(SP2), \ + mcps802154_rx_frame_config_name(SP3), \ + mcps802154_rx_frame_config_name(STS_MODE_MASK), \ + mcps802154_rx_frame_config_name(RANGING_ROUND) +TRACE_DEFINE_ENUM(MCPS802154_RX_FRAME_CONFIG_TIMESTAMP_DTU); +TRACE_DEFINE_ENUM(MCPS802154_RX_FRAME_CONFIG_AACK); +TRACE_DEFINE_ENUM(MCPS802154_RX_FRAME_CONFIG_RANGING); +TRACE_DEFINE_ENUM(MCPS802154_RX_FRAME_CONFIG_KEEP_RANGING_CLOCK); +TRACE_DEFINE_ENUM(MCPS802154_RX_FRAME_CONFIG_RANGING_PDOA); +TRACE_DEFINE_ENUM(MCPS802154_RX_FRAME_CONFIG_SP1); +TRACE_DEFINE_ENUM(MCPS802154_RX_FRAME_CONFIG_SP2); +TRACE_DEFINE_ENUM(MCPS802154_RX_FRAME_CONFIG_SP3); +TRACE_DEFINE_ENUM(MCPS802154_RX_FRAME_CONFIG_STS_MODE_MASK); +TRACE_DEFINE_ENUM(MCPS802154_RX_FRAME_CONFIG_RANGING_ROUND); #define mcps802154_rx_error_name(name) \ { \ MCPS802154_RX_ERROR_##name, #name \ } #define MCPS802154_RX_ERROR_NAMES \ - mcps802154_rx_error_name(BAD_CKSUM), \ - mcps802154_rx_error_name(UNCORRECTABLE), \ - mcps802154_rx_error_name(FILTERED), \ - mcps802154_rx_error_name(SFD_TIMEOUT), \ + mcps802154_rx_error_name(NONE), \ + mcps802154_rx_error_name(TIMEOUT), \ + mcps802154_rx_error_name(BAD_CKSUM), \ + mcps802154_rx_error_name(UNCORRECTABLE), \ + mcps802154_rx_error_name(FILTERED), \ + mcps802154_rx_error_name(SFD_TIMEOUT), \ + mcps802154_rx_error_name(PHR_DECODE), \ mcps802154_rx_error_name(OTHER) +TRACE_DEFINE_ENUM(MCPS802154_RX_ERROR_NONE); +TRACE_DEFINE_ENUM(MCPS802154_RX_ERROR_TIMEOUT); TRACE_DEFINE_ENUM(MCPS802154_RX_ERROR_BAD_CKSUM); TRACE_DEFINE_ENUM(MCPS802154_RX_ERROR_UNCORRECTABLE); TRACE_DEFINE_ENUM(MCPS802154_RX_ERROR_FILTERED); TRACE_DEFINE_ENUM(MCPS802154_RX_ERROR_SFD_TIMEOUT); +TRACE_DEFINE_ENUM(MCPS802154_RX_ERROR_PHR_DECODE); TRACE_DEFINE_ENUM(MCPS802154_RX_ERROR_OTHER); #define ieee802154_afilt_name(name) \ @@ -277,9 +284,9 @@ DEFINE_EVENT(local_only_evt, llhw_stop, TRACE_EVENT(llhw_tx_frame, TP_PROTO(const struct mcps802154_local *local, - const struct mcps802154_tx_frame_info *info, + const struct mcps802154_tx_frame_config *config, int frame_idx, int next_delay_dtu), - TP_ARGS(local, info, frame_idx, next_delay_dtu), + TP_ARGS(local, config, frame_idx, next_delay_dtu), TP_STRUCT__entry( LOCAL_ENTRY __field(u32, timestamp_dtu) @@ -292,11 +299,11 @@ TRACE_EVENT(llhw_tx_frame, ), TP_fast_assign( LOCAL_ASSIGN; - __entry->timestamp_dtu = info->timestamp_dtu; - __entry->rx_enable_after_tx_dtu = info->rx_enable_after_tx_dtu; - __entry->rx_enable_after_tx_timeout_dtu = info->rx_enable_after_tx_timeout_dtu; - __entry->ant_set_id = info->ant_set_id; - __entry->flags = info->flags; + __entry->timestamp_dtu = config->timestamp_dtu; + __entry->rx_enable_after_tx_dtu = config->rx_enable_after_tx_dtu; + __entry->rx_enable_after_tx_timeout_dtu = config->rx_enable_after_tx_timeout_dtu; + __entry->ant_set_id = config->ant_set_id; + __entry->flags = config->flags; __entry->frame_idx = frame_idx; __entry->next_delay_dtu = next_delay_dtu; ), @@ -306,7 +313,7 @@ TRACE_EVENT(llhw_tx_frame, LOCAL_PR_ARG, __entry->timestamp_dtu, __entry->rx_enable_after_tx_dtu, __entry->rx_enable_after_tx_timeout_dtu, __entry->ant_set_id, - __print_flags(__entry->flags, "|", MCPS802154_TX_FRAME_NAMES), + __print_flags(__entry->flags, "|", MCPS802154_TX_FRAME_CONFIG_NAMES), __entry->frame_idx, __entry->next_delay_dtu ) @@ -314,13 +321,14 @@ TRACE_EVENT(llhw_tx_frame, TRACE_EVENT(llhw_rx_enable, TP_PROTO(const struct mcps802154_local *local, - const struct mcps802154_rx_info *info, + const struct mcps802154_rx_frame_config *config, int frame_idx, int next_delay_dtu), - TP_ARGS(local, info, frame_idx, next_delay_dtu), + TP_ARGS(local, config, frame_idx, next_delay_dtu), TP_STRUCT__entry( LOCAL_ENTRY __field(u32, timestamp_dtu) __field(int, timeout_dtu) + __field(int, frame_timeout_dtu) __field(u8, flags) __field(int, ant_set_id) __field(int, frame_idx) @@ -328,19 +336,21 @@ TRACE_EVENT(llhw_rx_enable, ), TP_fast_assign( LOCAL_ASSIGN; - __entry->timestamp_dtu = info->timestamp_dtu; - __entry->timeout_dtu = info->timeout_dtu; - __entry->flags = info->flags; - __entry->ant_set_id = info->ant_set_id; + __entry->timestamp_dtu = config->timestamp_dtu; + __entry->timeout_dtu = config->timeout_dtu; + __entry->frame_timeout_dtu = config->frame_timeout_dtu; + __entry->flags = config->flags; + __entry->ant_set_id = config->ant_set_id; __entry->frame_idx = frame_idx; __entry->next_delay_dtu = next_delay_dtu; ), TP_printk(LOCAL_PR_FMT " timestamp_dtu=0x%08x timeout_dtu=%d" - " ant_set_id=%d flags=%s frame_idx=%d next_delay_dtu=%d", + " frame_timeout_dtu=%d ant_set_id=%d flags=%s frame_idx=%d" + " next_delay_dtu=%d", LOCAL_PR_ARG, __entry->timestamp_dtu, __entry->timeout_dtu, - __entry->ant_set_id, - __print_flags(__entry->flags, "|", MCPS802154_RX_INFO_NAMES), + __entry->frame_timeout_dtu, __entry->ant_set_id, + __print_flags(__entry->flags, "|", MCPS802154_RX_FRAME_CONFIG_NAMES), __entry->frame_idx, __entry->next_delay_dtu ) @@ -434,28 +444,53 @@ TRACE_EVENT(llhw_set_channel, ); TRACE_EVENT(llhw_set_hrp_uwb_params, - TP_PROTO(const struct mcps802154_local *local, int prf, int psr, - int sfd_selector, int phr_rate, int data_rate), - TP_ARGS(local, prf, psr, sfd_selector, phr_rate, data_rate), + TP_PROTO(const struct mcps802154_local *local, + const struct mcps802154_hrp_uwb_params *params), + TP_ARGS(local, params), + TP_STRUCT__entry( + LOCAL_ENTRY + __field(int, prf) + __field(int, psr) + __field(int, sfd_selector) + __field(int, phr_hi_rate) + __field(int, data_rate) + ), + TP_fast_assign( + LOCAL_ASSIGN; + __entry->prf = params->prf; + __entry->psr = params->psr; + __entry->sfd_selector = params->sfd_selector; + __entry->phr_hi_rate = params->phr_hi_rate; + __entry->data_rate = params->data_rate; + ), + TP_printk(LOCAL_PR_FMT " prf=%d psr=%d sfd_selector=%d phr_hi_rate=%d data_rate=%d", + LOCAL_PR_ARG, __entry->prf, __entry->psr, + __entry->sfd_selector, __entry->phr_hi_rate, __entry->data_rate) + ); + +TRACE_EVENT(llhw_check_hrp_uwb_params, + TP_PROTO(const struct mcps802154_local *local, + const struct mcps802154_hrp_uwb_params *params), + TP_ARGS(local, params), TP_STRUCT__entry( LOCAL_ENTRY __field(int, prf) __field(int, psr) __field(int, sfd_selector) - __field(int, phr_rate) + __field(int, phr_hi_rate) __field(int, data_rate) ), TP_fast_assign( LOCAL_ASSIGN; - __entry->prf = prf; - __entry->psr = psr; - __entry->sfd_selector = sfd_selector; - __entry->phr_rate = phr_rate; - __entry->data_rate = data_rate; + __entry->prf = params->prf; + __entry->psr = params->psr; + __entry->sfd_selector = params->sfd_selector; + __entry->phr_hi_rate = params->phr_hi_rate; + __entry->data_rate = params->data_rate; ), - TP_printk(LOCAL_PR_FMT " prf=%d psr=%d sfd_selector=%d phr_rate=%d data_rate=%d", + TP_printk(LOCAL_PR_FMT " prf=%d psr=%d sfd_selector=%d phr_hi_rate=%d data_rate=%d", LOCAL_PR_ARG, __entry->prf, __entry->psr, - __entry->sfd_selector, __entry->phr_rate, __entry->data_rate) + __entry->sfd_selector, __entry->phr_hi_rate, __entry->data_rate) ); TRACE_EVENT(llhw_set_sts_params, @@ -485,6 +520,48 @@ TRACE_EVENT(llhw_set_sts_params, __entry->sp2_rx_gap_4chips[3]) ); +TRACE_EVENT(llhw_rx_get_measurement, + TP_PROTO(const struct mcps802154_local *local, const void *rx_ctx), + TP_ARGS(local, rx_ctx), + TP_STRUCT__entry( + LOCAL_ENTRY + __field(const void *, rx_ctx) + ), + TP_fast_assign( + LOCAL_ASSIGN; + __entry->rx_ctx = rx_ctx; + ), + TP_printk(LOCAL_PR_FMT " rx_ctx=%p", + LOCAL_PR_ARG, + __entry->rx_ctx) + ); + +TRACE_EVENT(llhw_return_measurement, + TP_PROTO(const struct mcps802154_local *local, int r, + const struct mcps802154_rx_measurement_info *info), + TP_ARGS(local, r, info), + TP_STRUCT__entry( + LOCAL_ENTRY + __field(int, r) + __field(int, n_rssis) + __field(int, n_aoas) + __field(int, n_cirs) + ), + TP_fast_assign( + LOCAL_ASSIGN; + __entry->r = r; + __entry->n_rssis = info->n_rssis; + __entry->n_aoas = info->n_aoas; + __entry->n_cirs = info->n_cirs; + ), + TP_printk(LOCAL_PR_FMT " r=%d n_rssis=%d n_aoas=%d n_cirs=%d", + LOCAL_PR_ARG, + __entry->r, + __entry->n_rssis, + __entry->n_aoas, + __entry->n_cirs) + ); + TRACE_EVENT(llhw_set_hw_addr_filt, TP_PROTO(const struct mcps802154_local *local, const struct ieee802154_hw_addr_filt *filt, @@ -810,6 +887,18 @@ TRACE_EVENT(ca_return_int, TP_printk(LOCAL_PR_FMT " r=%d", LOCAL_PR_ARG, __entry->r) ); +TRACE_EVENT(fproc_broken_enter, + TP_PROTO(const struct mcps802154_local *local), + TP_ARGS(local), + TP_STRUCT__entry( + LOCAL_ENTRY + ), + TP_fast_assign( + LOCAL_ASSIGN; + ), + TP_printk(LOCAL_PR_FMT, LOCAL_PR_ARG) + ); + TRACE_EVENT(schedule_update, TP_PROTO(const struct mcps802154_local *local, u32 next_timestamp_dtu), TP_ARGS(local, next_timestamp_dtu), @@ -825,6 +914,38 @@ TRACE_EVENT(schedule_update, __entry->next_timestamp_dtu) ); +TRACE_EVENT(update_schedule_error, + TP_PROTO(const struct mcps802154_local *local, + const struct mcps802154_schedule_update *su, + u32 next_timestamp_dtu), + TP_ARGS(local, su, next_timestamp_dtu), + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u32, expected_start_timestamp_dtu) + __field(u32, start_timestamp_dtu) + __field(int, duration_dtu) + __field(size_t, n_regions) + __field(u32, next_timestamp_dtu) + ), + TP_fast_assign( + LOCAL_ASSIGN; + __entry->expected_start_timestamp_dtu = su->expected_start_timestamp_dtu; + __entry->start_timestamp_dtu = su->start_timestamp_dtu; + __entry->duration_dtu = su->duration_dtu; + __entry->n_regions = su->n_regions; + __entry->next_timestamp_dtu = next_timestamp_dtu; + ), + TP_printk(LOCAL_PR_FMT " expected_start_timestamp_dtu=0x%08x " + "start_timestamp_dtu=0x%08x duration_dtu=%d " + "n_regions=%lu next_timestamp_dtu=0x%08x", + LOCAL_PR_ARG, + __entry->expected_start_timestamp_dtu, + __entry->start_timestamp_dtu, + __entry->duration_dtu, + __entry->n_regions, + __entry->next_timestamp_dtu) + ); + TRACE_EVENT(schedule_update_done, TP_PROTO(const struct mcps802154_local *local, const struct mcps802154_schedule *sched), @@ -846,6 +967,63 @@ TRACE_EVENT(schedule_update_done, __entry->duration_dtu, __entry->n_regions) ); +TRACE_EVENT( + region_notify_stop, + TP_PROTO(const struct mcps802154_region *region), + TP_ARGS(region), + TP_STRUCT__entry( + __string(region_name, region->ops->name) + ), + TP_fast_assign( + __assign_str(region_name, region->ops->name); + ), + TP_printk("region=%s", + __get_str(region_name) + ) + ); + +TRACE_EVENT( + region_get_demand, + TP_PROTO(const struct mcps802154_region *region, + u32 next_timestamp_dtu), + TP_ARGS(region, next_timestamp_dtu), + TP_STRUCT__entry( + __string(region_name, region->ops->name) + __field(u32, next_timestamp_dtu) + ), + TP_fast_assign( + __assign_str(region_name, region->ops->name); + __entry->next_timestamp_dtu = next_timestamp_dtu; + ), + TP_printk("region=%s next_timestamp_dtu=%#x", + __get_str(region_name), + __entry->next_timestamp_dtu + ) + ); + +TRACE_EVENT( + region_get_demand_return, + TP_PROTO(const struct mcps802154_region *region, + const struct mcps802154_region_demand *demand, + int r), + TP_ARGS(region, demand, r), + TP_STRUCT__entry( + __field(int, r) + __field(u32, timestamp_dtu) + __field(u32, max_duration_dtu) + ), + TP_fast_assign( + __entry->r = r; + __entry->timestamp_dtu = demand->timestamp_dtu; + __entry->max_duration_dtu = demand->max_duration_dtu; + ), + TP_printk("r=%d timestamp_dtu=%#x max_duration_dtu=%d", + __entry->r, + __entry->timestamp_dtu, + __entry->max_duration_dtu + ) + ); + TRACE_EVENT(region_get_access, TP_PROTO(const struct mcps802154_local *local, const struct mcps802154_region *region, @@ -873,6 +1051,40 @@ TRACE_EVENT(region_get_access, __entry->next_in_region_dtu, __entry->region_duration_dtu) ); +TRACE_EVENT( + region_idle_params, + TP_PROTO(const struct idle_params *params), + TP_ARGS(params), + TP_STRUCT__entry( + __field(s32, min_duration_dtu) + __field(s32, max_duration_dtu) + ), + TP_fast_assign( + __entry->min_duration_dtu = params->min_duration_dtu; + __entry->max_duration_dtu = params->max_duration_dtu; + ), + TP_printk("min_duration_dtu=%d max_duration_dtu=%d", + __entry->min_duration_dtu, + __entry->max_duration_dtu) +); + +TRACE_EVENT( + region_idle_get_access, + TP_PROTO(u32 timestamp_dtu, int duration_dtu), + TP_ARGS(timestamp_dtu, duration_dtu), + TP_STRUCT__entry( + __field(u32, timestamp_dtu) + __field(int, duration_dtu) + ), + TP_fast_assign( + __entry->timestamp_dtu = timestamp_dtu; + __entry->duration_dtu = duration_dtu; + ), + TP_printk("timestamp_dtu=%#x duration_dtu=%d", + __entry->timestamp_dtu, + __entry->duration_dtu) +); + #endif /* !TRACE_H || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH |