| /* |
| * 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; |
| unsigned int rx_ant_pair_azimuth; |
| unsigned int rx_ant_pair_elevation; |
| bool is_same_rx_ant; |
| 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 = local->is_same_rx_ant, |
| }; |
| 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 (!pskb_may_pull(skb, 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 (!pskb_may_pull(skb, 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 (!pskb_may_pull(skb, 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 (!pskb_may_pull(skb, 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 (!pskb_may_pull(skb, 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 (!pskb_may_pull(skb, 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 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 = local->is_same_rx_ant; |
| |
| 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, |
| .ant_pair_id = local->rx_ant_pair_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_id = local->tx_ant, |
| }, |
| }; |
| |
| 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_pair_id = local->rx_ant_pair_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_id = local->tx_ant, |
| }, |
| }; |
| |
| 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, |
| .ant_id = local->tx_ant, |
| }, |
| .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); |
| |
| 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_pair_id = local->rx_ant_pair_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_id = local->tx_ant, |
| }, |
| }; |
| 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); |
| |
| 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_pair_id = local->rx_ant_pair_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 = TX_ANT_ID_DEFAULT; |
| local->rx_ant_pair_azimuth = RX_ANT_PAIR_ID_DEFAULT; |
| local->rx_ant_pair_elevation = RX_ANT_PAIR_ID_DEFAULT; |
| local->is_same_rx_ant = 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 = 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_pair_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_pair_elevation = id; |
| } |
| |
| local->is_same_rx_ant = local->rx_ant_pair_azimuth == |
| local->rx_ant_pair_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); |
| } |