diff options
author | 2023-02-21 18:24:12 -0800 | |
---|---|---|
committer | 2023-02-21 18:24:12 -0800 | |
commit | 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch) | |
tree | cc5c2d0a898769fd59549594fedb3ee6f84e59a0 /tools/testing/selftests/nci/nci_dev.c | |
download | linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip |
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
Pull networking updates from Jakub Kicinski:
"Core:
- Add dedicated kmem_cache for typical/small skb->head, avoid having
to access struct page at kfree time, and improve memory use.
- Introduce sysctl to set default RPS configuration for new netdevs.
- Define Netlink protocol specification format which can be used to
describe messages used by each family and auto-generate parsers.
Add tools for generating kernel data structures and uAPI headers.
- Expose all net/core sysctls inside netns.
- Remove 4s sleep in netpoll if carrier is instantly detected on
boot.
- Add configurable limit of MDB entries per port, and port-vlan.
- Continue populating drop reasons throughout the stack.
- Retire a handful of legacy Qdiscs and classifiers.
Protocols:
- Support IPv4 big TCP (TSO frames larger than 64kB).
- Add IP_LOCAL_PORT_RANGE socket option, to control local port range
on socket by socket basis.
- Track and report in procfs number of MPTCP sockets used.
- Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path
manager.
- IPv6: don't check net.ipv6.route.max_size and rely on garbage
collection to free memory (similarly to IPv4).
- Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986).
- ICMP: add per-rate limit counters.
- Add support for user scanning requests in ieee802154.
- Remove static WEP support.
- Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate
reporting.
- WiFi 7 EHT channel puncturing support (client & AP).
BPF:
- Add a rbtree data structure following the "next-gen data structure"
precedent set by recently added linked list, that is, by using
kfunc + kptr instead of adding a new BPF map type.
- Expose XDP hints via kfuncs with initial support for RX hash and
timestamp metadata.
- Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to
better support decap on GRE tunnel devices not operating in collect
metadata.
- Improve x86 JIT's codegen for PROBE_MEM runtime error checks.
- Remove the need for trace_printk_lock for bpf_trace_printk and
bpf_trace_vprintk helpers.
- Extend libbpf's bpf_tracing.h support for tracing arguments of
kprobes/uprobes and syscall as a special case.
- Significantly reduce the search time for module symbols by
livepatch and BPF.
- Enable cpumasks to be used as kptrs, which is useful for tracing
programs tracking which tasks end up running on which CPUs in
different time intervals.
- Add support for BPF trampoline on s390x and riscv64.
- Add capability to export the XDP features supported by the NIC.
- Add __bpf_kfunc tag for marking kernel functions as kfuncs.
- Add cgroup.memory=nobpf kernel parameter option to disable BPF
memory accounting for container environments.
Netfilter:
- Remove the CLUSTERIP target. It has been marked as obsolete for
years, and we still have WARN splats wrt races of the out-of-band
/proc interface installed by this target.
- Add 'destroy' commands to nf_tables. They are identical to the
existing 'delete' commands, but do not return an error if the
referenced object (set, chain, rule...) did not exist.
Driver API:
- Improve cpumask_local_spread() locality to help NICs set the right
IRQ affinity on AMD platforms.
- Separate C22 and C45 MDIO bus transactions more clearly.
- Introduce new DCB table to control DSCP rewrite on egress.
- Support configuration of Physical Layer Collision Avoidance (PLCA)
Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of
shared medium Ethernet.
- Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing
preemption of low priority frames by high priority frames.
- Add support for controlling MACSec offload using netlink SET.
- Rework devlink instance refcounts to allow registration and
de-registration under the instance lock. Split the code into
multiple files, drop some of the unnecessarily granular locks and
factor out common parts of netlink operation handling.
- Add TX frame aggregation parameters (for USB drivers).
- Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning
messages with notifications for debug.
- Allow offloading of UDP NEW connections via act_ct.
- Add support for per action HW stats in TC.
- Support hardware miss to TC action (continue processing in SW from
a specific point in the action chain).
- Warn if old Wireless Extension user space interface is used with
modern cfg80211/mac80211 drivers. Do not support Wireless
Extensions for Wi-Fi 7 devices at all. Everyone should switch to
using nl80211 interface instead.
- Improve the CAN bit timing configuration. Use extack to return
error messages directly to user space, update the SJW handling,
including the definition of a new default value that will benefit
CAN-FD controllers, by increasing their oscillator tolerance.
New hardware / drivers:
- Ethernet:
- nVidia BlueField-3 support (control traffic driver)
- Ethernet support for imx93 SoCs
- Motorcomm yt8531 gigabit Ethernet PHY
- onsemi NCN26000 10BASE-T1S PHY (with support for PLCA)
- Microchip LAN8841 PHY (incl. cable diagnostics and PTP)
- Amlogic gxl MDIO mux
- WiFi:
- RealTek RTL8188EU (rtl8xxxu)
- Qualcomm Wi-Fi 7 devices (ath12k)
- CAN:
- Renesas R-Car V4H
Drivers:
- Bluetooth:
- Set Per Platform Antenna Gain (PPAG) for Intel controllers.
- Ethernet NICs:
- Intel (1G, igc):
- support TSN / Qbv / packet scheduling features of i226 model
- Intel (100G, ice):
- use GNSS subsystem instead of TTY
- multi-buffer XDP support
- extend support for GPIO pins to E823 devices
- nVidia/Mellanox:
- update the shared buffer configuration on PFC commands
- implement PTP adjphase function for HW offset control
- TC support for Geneve and GRE with VF tunnel offload
- more efficient crypto key management method
- multi-port eswitch support
- Netronome/Corigine:
- add DCB IEEE support
- support IPsec offloading for NFP3800
- Freescale/NXP (enetc):
- support XDP_REDIRECT for XDP non-linear buffers
- improve reconfig, avoid link flap and waiting for idle
- support MAC Merge layer
- Other NICs:
- sfc/ef100: add basic devlink support for ef100
- ionic: rx_push mode operation (writing descriptors via MMIO)
- bnxt: use the auxiliary bus abstraction for RDMA
- r8169: disable ASPM and reset bus in case of tx timeout
- cpsw: support QSGMII mode for J721e CPSW9G
- cpts: support pulse-per-second output
- ngbe: add an mdio bus driver
- usbnet: optimize usbnet_bh() by avoiding unnecessary queuing
- r8152: handle devices with FW with NCM support
- amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation
- virtio-net: support multi buffer XDP
- virtio/vsock: replace virtio_vsock_pkt with sk_buff
- tsnep: XDP support
- Ethernet high-speed switches:
- nVidia/Mellanox (mlxsw):
- add support for latency TLV (in FW control messages)
- Microchip (sparx5):
- separate explicit and implicit traffic forwarding rules, make
the implicit rules always active
- add support for egress DSCP rewrite
- IS0 VCAP support (Ingress Classification)
- IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS
etc.)
- ES2 VCAP support (Egress Access Control)
- support for Per-Stream Filtering and Policing (802.1Q,
8.6.5.1)
- Ethernet embedded switches:
- Marvell (mv88e6xxx):
- add MAB (port auth) offload support
- enable PTP receive for mv88e6390
- NXP (ocelot):
- support MAC Merge layer
- support for the the vsc7512 internal copper phys
- Microchip:
- lan9303: convert to PHYLINK
- lan966x: support TC flower filter statistics
- lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x
- lan937x: support Credit Based Shaper configuration
- ksz9477: support Energy Efficient Ethernet
- other:
- qca8k: convert to regmap read/write API, use bulk operations
- rswitch: Improve TX timestamp accuracy
- Intel WiFi (iwlwifi):
- EHT (Wi-Fi 7) rate reporting
- STEP equalizer support: transfer some STEP (connection to radio
on platforms with integrated wifi) related parameters from the
BIOS to the firmware.
- Qualcomm 802.11ax WiFi (ath11k):
- IPQ5018 support
- Fine Timing Measurement (FTM) responder role support
- channel 177 support
- MediaTek WiFi (mt76):
- per-PHY LED support
- mt7996: EHT (Wi-Fi 7) support
- Wireless Ethernet Dispatch (WED) reset support
- switch to using page pool allocator
- RealTek WiFi (rtw89):
- support new version of Bluetooth co-existance
- Mobile:
- rmnet: support TX aggregation"
* tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits)
page_pool: add a comment explaining the fragment counter usage
net: ethtool: fix __ethtool_dev_mm_supported() implementation
ethtool: pse-pd: Fix double word in comments
xsk: add linux/vmalloc.h to xsk.c
sefltests: netdevsim: wait for devlink instance after netns removal
selftest: fib_tests: Always cleanup before exit
net/mlx5e: Align IPsec ASO result memory to be as required by hardware
net/mlx5e: TC, Set CT miss to the specific ct action instance
net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG
net/mlx5: Refactor tc miss handling to a single function
net/mlx5: Kconfig: Make tc offload depend on tc skb extension
net/sched: flower: Support hardware miss to tc action
net/sched: flower: Move filter handle initialization earlier
net/sched: cls_api: Support hardware miss to tc action
net/sched: Rename user cookie and act cookie
sfc: fix builds without CONFIG_RTC_LIB
sfc: clean up some inconsistent indentings
net/mlx4_en: Introduce flexible array to silence overflow warning
net: lan966x: Fix possible deadlock inside PTP
net/ulp: Remove redundant ->clone() test in inet_clone_ulp().
...
Diffstat (limited to 'tools/testing/selftests/nci/nci_dev.c')
-rw-r--r-- | tools/testing/selftests/nci/nci_dev.c | 904 |
1 files changed, 904 insertions, 0 deletions
diff --git a/tools/testing/selftests/nci/nci_dev.c b/tools/testing/selftests/nci/nci_dev.c new file mode 100644 index 000000000..1562aa7d6 --- /dev/null +++ b/tools/testing/selftests/nci/nci_dev.c @@ -0,0 +1,904 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 Samsung Electrnoics + * Bongsu Jeon <bongsu.jeon@samsung.com> + * + * Test code for nci + */ + +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <pthread.h> +#include <linux/genetlink.h> +#include <sys/socket.h> +#include <linux/nfc.h> + +#include "../kselftest_harness.h" + +#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN)) +#define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN) +#define NLA_DATA(na) ((void *)((char *)(na) + NLA_HDRLEN)) +#define NLA_PAYLOAD(len) ((len) - NLA_HDRLEN) + +#define MAX_MSG_SIZE 1024 + +#define IOCTL_GET_NCIDEV_IDX 0 +#define VIRTUAL_NFC_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \ + NFC_PROTO_MIFARE_MASK | \ + NFC_PROTO_FELICA_MASK | \ + NFC_PROTO_ISO14443_MASK | \ + NFC_PROTO_ISO14443_B_MASK | \ + NFC_PROTO_ISO15693_MASK) + +const __u8 nci_reset_cmd[] = {0x20, 0x00, 0x01, 0x01}; +const __u8 nci_init_cmd[] = {0x20, 0x01, 0x00}; +const __u8 nci_rf_discovery_cmd[] = {0x21, 0x03, 0x09, 0x04, 0x00, 0x01, + 0x01, 0x01, 0x02, 0x01, 0x06, 0x01}; +const __u8 nci_init_cmd_v2[] = {0x20, 0x01, 0x02, 0x00, 0x00}; +const __u8 nci_rf_disc_map_cmd[] = {0x21, 0x00, 0x07, 0x02, 0x04, 0x03, + 0x02, 0x05, 0x03, 0x03}; +const __u8 nci_rf_deact_cmd[] = {0x21, 0x06, 0x01, 0x00}; +const __u8 nci_reset_rsp[] = {0x40, 0x00, 0x03, 0x00, 0x10, 0x01}; +const __u8 nci_reset_rsp_v2[] = {0x40, 0x00, 0x01, 0x00}; +const __u8 nci_reset_ntf[] = {0x60, 0x00, 0x09, 0x02, 0x01, 0x20, 0x0e, + 0x04, 0x61, 0x00, 0x04, 0x02}; +const __u8 nci_init_rsp[] = {0x40, 0x01, 0x14, 0x00, 0x02, 0x0e, 0x02, + 0x00, 0x03, 0x01, 0x02, 0x03, 0x02, 0xc8, + 0x00, 0xff, 0x10, 0x00, 0x0e, 0x12, 0x00, + 0x00, 0x04}; +const __u8 nci_init_rsp_v2[] = {0x40, 0x01, 0x1c, 0x00, 0x1a, 0x7e, 0x06, + 0x00, 0x02, 0x92, 0x04, 0xff, 0xff, 0x01, + 0x00, 0x40, 0x06, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x02, 0x00, 0x03, 0x01, 0x01, 0x06, + 0x00, 0x80, 0x00}; +const __u8 nci_rf_disc_map_rsp[] = {0x41, 0x00, 0x01, 0x00}; +const __u8 nci_rf_disc_rsp[] = {0x41, 0x03, 0x01, 0x00}; +const __u8 nci_rf_deact_rsp[] = {0x41, 0x06, 0x01, 0x00}; +const __u8 nci_rf_deact_ntf[] = {0x61, 0x06, 0x02, 0x00, 0x00}; +const __u8 nci_rf_activate_ntf[] = {0x61, 0x05, 0x1D, 0x01, 0x02, 0x04, 0x00, + 0xFF, 0xFF, 0x0C, 0x44, 0x03, 0x07, 0x04, + 0x62, 0x26, 0x11, 0x80, 0x1D, 0x80, 0x01, + 0x20, 0x00, 0x00, 0x00, 0x06, 0x05, 0x75, + 0x77, 0x81, 0x02, 0x80}; +const __u8 nci_t4t_select_cmd[] = {0x00, 0x00, 0x0C, 0x00, 0xA4, 0x04, 0x00, + 0x07, 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01}; +const __u8 nci_t4t_select_cmd2[] = {0x00, 0x00, 0x07, 0x00, 0xA4, 0x00, 0x0C, 0x02, + 0xE1, 0x03}; +const __u8 nci_t4t_select_cmd3[] = {0x00, 0x00, 0x07, 0x00, 0xA4, 0x00, 0x0C, 0x02, + 0xE1, 0x04}; +const __u8 nci_t4t_read_cmd[] = {0x00, 0x00, 0x05, 0x00, 0xB0, 0x00, 0x00, 0x0F}; +const __u8 nci_t4t_read_rsp[] = {0x00, 0x00, 0x11, 0x00, 0x0F, 0x20, 0x00, 0x3B, + 0x00, 0x34, 0x04, 0x06, 0xE1, 0x04, 0x08, 0x00, + 0x00, 0x00, 0x90, 0x00}; +const __u8 nci_t4t_read_cmd2[] = {0x00, 0x00, 0x05, 0x00, 0xB0, 0x00, 0x00, 0x02}; +const __u8 nci_t4t_read_rsp2[] = {0x00, 0x00, 0x04, 0x00, 0x0F, 0x90, 0x00}; +const __u8 nci_t4t_read_cmd3[] = {0x00, 0x00, 0x05, 0x00, 0xB0, 0x00, 0x02, 0x0F}; +const __u8 nci_t4t_read_rsp3[] = {0x00, 0x00, 0x11, 0xD1, 0x01, 0x0B, 0x54, 0x02, + 0x65, 0x6E, 0x4E, 0x46, 0x43, 0x20, 0x54, 0x45, + 0x53, 0x54, 0x90, 0x00}; +const __u8 nci_t4t_rsp_ok[] = {0x00, 0x00, 0x02, 0x90, 0x00}; + +struct msgtemplate { + struct nlmsghdr n; + struct genlmsghdr g; + char buf[MAX_MSG_SIZE]; +}; + +static int create_nl_socket(void) +{ + int fd; + struct sockaddr_nl local; + + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (fd < 0) + return -1; + + memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + + if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) + goto error; + + return fd; +error: + close(fd); + return -1; +} + +static int send_cmd_mt_nla(int sd, __u16 nlmsg_type, __u32 nlmsg_pid, + __u8 genl_cmd, int nla_num, __u16 nla_type[], + void *nla_data[], int nla_len[], __u16 flags) +{ + struct sockaddr_nl nladdr; + struct msgtemplate msg; + struct nlattr *na; + int cnt, prv_len; + int r, buflen; + char *buf; + + msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); + msg.n.nlmsg_type = nlmsg_type; + msg.n.nlmsg_flags = flags; + msg.n.nlmsg_seq = 0; + msg.n.nlmsg_pid = nlmsg_pid; + msg.g.cmd = genl_cmd; + msg.g.version = 0x1; + + prv_len = 0; + for (cnt = 0; cnt < nla_num; cnt++) { + na = (struct nlattr *)(GENLMSG_DATA(&msg) + prv_len); + na->nla_type = nla_type[cnt]; + na->nla_len = nla_len[cnt] + NLA_HDRLEN; + + if (nla_len[cnt] > 0) + memcpy(NLA_DATA(na), nla_data[cnt], nla_len[cnt]); + + prv_len = NLA_ALIGN(nla_len[cnt]) + NLA_HDRLEN; + msg.n.nlmsg_len += prv_len; + } + + buf = (char *)&msg; + buflen = msg.n.nlmsg_len; + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + while ((r = sendto(sd, buf, buflen, 0, (struct sockaddr *)&nladdr, + sizeof(nladdr))) < buflen) { + if (r > 0) { + buf += r; + buflen -= r; + } else if (errno != EAGAIN) { + return -1; + } + } + return 0; +} + +static int send_get_nfc_family(int sd, __u32 pid) +{ + __u16 nla_get_family_type = CTRL_ATTR_FAMILY_NAME; + void *nla_get_family_data; + int nla_get_family_len; + char family_name[100]; + + nla_get_family_len = strlen(NFC_GENL_NAME) + 1; + strcpy(family_name, NFC_GENL_NAME); + nla_get_family_data = family_name; + + return send_cmd_mt_nla(sd, GENL_ID_CTRL, pid, CTRL_CMD_GETFAMILY, + 1, &nla_get_family_type, &nla_get_family_data, + &nla_get_family_len, NLM_F_REQUEST); +} + +static int get_family_id(int sd, __u32 pid, __u32 *event_group) +{ + struct { + struct nlmsghdr n; + struct genlmsghdr g; + char buf[512]; + } ans; + struct nlattr *na; + int resp_len; + __u16 id; + int len; + int rc; + + rc = send_get_nfc_family(sd, pid); + + if (rc < 0) + return 0; + + resp_len = recv(sd, &ans, sizeof(ans), 0); + + if (ans.n.nlmsg_type == NLMSG_ERROR || resp_len < 0 || + !NLMSG_OK(&ans.n, resp_len)) + return 0; + + len = 0; + resp_len = GENLMSG_PAYLOAD(&ans.n); + na = (struct nlattr *)GENLMSG_DATA(&ans); + + while (len < resp_len) { + len += NLA_ALIGN(na->nla_len); + if (na->nla_type == CTRL_ATTR_FAMILY_ID) { + id = *(__u16 *)NLA_DATA(na); + } else if (na->nla_type == CTRL_ATTR_MCAST_GROUPS) { + struct nlattr *nested_na; + struct nlattr *group_na; + int group_attr_len; + int group_attr; + + nested_na = (struct nlattr *)((char *)na + NLA_HDRLEN); + group_na = (struct nlattr *)((char *)nested_na + NLA_HDRLEN); + group_attr_len = 0; + + for (group_attr = CTRL_ATTR_MCAST_GRP_UNSPEC; + group_attr < CTRL_ATTR_MCAST_GRP_MAX; group_attr++) { + if (group_na->nla_type == CTRL_ATTR_MCAST_GRP_ID) { + *event_group = *(__u32 *)((char *)group_na + + NLA_HDRLEN); + break; + } + + group_attr_len += NLA_ALIGN(group_na->nla_len) + + NLA_HDRLEN; + if (group_attr_len >= nested_na->nla_len) + break; + + group_na = (struct nlattr *)((char *)group_na + + NLA_ALIGN(group_na->nla_len)); + } + } + na = (struct nlattr *)(GENLMSG_DATA(&ans) + len); + } + return id; +} + +static int send_cmd_with_idx(int sd, __u16 nlmsg_type, __u32 nlmsg_pid, + __u8 genl_cmd, int dev_id) +{ + __u16 nla_type = NFC_ATTR_DEVICE_INDEX; + void *nla_data = &dev_id; + int nla_len = 4; + + return send_cmd_mt_nla(sd, nlmsg_type, nlmsg_pid, genl_cmd, 1, + &nla_type, &nla_data, &nla_len, NLM_F_REQUEST); +} + +static int get_nci_devid(int sd, __u16 fid, __u32 pid, int dev_id, struct msgtemplate *msg) +{ + int rc, resp_len; + + rc = send_cmd_with_idx(sd, fid, pid, NFC_CMD_GET_DEVICE, dev_id); + if (rc < 0) { + rc = -1; + goto error; + } + + resp_len = recv(sd, msg, sizeof(*msg), 0); + if (resp_len < 0) { + rc = -2; + goto error; + } + + if (msg->n.nlmsg_type == NLMSG_ERROR || + !NLMSG_OK(&msg->n, resp_len)) { + rc = -3; + goto error; + } + + return 0; +error: + return rc; +} + +static __u8 get_dev_enable_state(struct msgtemplate *msg) +{ + struct nlattr *na; + int resp_len; + int len; + + resp_len = GENLMSG_PAYLOAD(&msg->n); + na = (struct nlattr *)GENLMSG_DATA(msg); + len = 0; + + while (len < resp_len) { + len += NLA_ALIGN(na->nla_len); + if (na->nla_type == NFC_ATTR_DEVICE_POWERED) + return *(char *)NLA_DATA(na); + na = (struct nlattr *)(GENLMSG_DATA(msg) + len); + } + + return resp_len; +} + +FIXTURE(NCI) { + int virtual_nci_fd; + bool open_state; + int dev_idex; + bool isNCI2; + int proto; + __u32 pid; + __u16 fid; + int sd; +}; + +FIXTURE_VARIANT(NCI) { + bool isNCI2; +}; + +FIXTURE_VARIANT_ADD(NCI, NCI1_0) { + .isNCI2 = false, +}; + +FIXTURE_VARIANT_ADD(NCI, NCI2_0) { + .isNCI2 = true, +}; + +static void *virtual_dev_open(void *data) +{ + char buf[258]; + int dev_fd; + int len; + + dev_fd = *(int *)data; + + len = read(dev_fd, buf, 258); + if (len <= 0) + goto error; + if (len != sizeof(nci_reset_cmd)) + goto error; + if (memcmp(nci_reset_cmd, buf, len)) + goto error; + write(dev_fd, nci_reset_rsp, sizeof(nci_reset_rsp)); + + len = read(dev_fd, buf, 258); + if (len <= 0) + goto error; + if (len != sizeof(nci_init_cmd)) + goto error; + if (memcmp(nci_init_cmd, buf, len)) + goto error; + write(dev_fd, nci_init_rsp, sizeof(nci_init_rsp)); + + len = read(dev_fd, buf, 258); + if (len <= 0) + goto error; + if (len != sizeof(nci_rf_disc_map_cmd)) + goto error; + if (memcmp(nci_rf_disc_map_cmd, buf, len)) + goto error; + write(dev_fd, nci_rf_disc_map_rsp, sizeof(nci_rf_disc_map_rsp)); + + return (void *)0; +error: + return (void *)-1; +} + +static void *virtual_dev_open_v2(void *data) +{ + char buf[258]; + int dev_fd; + int len; + + dev_fd = *(int *)data; + + len = read(dev_fd, buf, 258); + if (len <= 0) + goto error; + if (len != sizeof(nci_reset_cmd)) + goto error; + if (memcmp(nci_reset_cmd, buf, len)) + goto error; + write(dev_fd, nci_reset_rsp_v2, sizeof(nci_reset_rsp_v2)); + write(dev_fd, nci_reset_ntf, sizeof(nci_reset_ntf)); + + len = read(dev_fd, buf, 258); + if (len <= 0) + goto error; + if (len != sizeof(nci_init_cmd_v2)) + goto error; + if (memcmp(nci_init_cmd_v2, buf, len)) + goto error; + write(dev_fd, nci_init_rsp_v2, sizeof(nci_init_rsp_v2)); + + len = read(dev_fd, buf, 258); + if (len <= 0) + goto error; + if (len != sizeof(nci_rf_disc_map_cmd)) + goto error; + if (memcmp(nci_rf_disc_map_cmd, buf, len)) + goto error; + write(dev_fd, nci_rf_disc_map_rsp, sizeof(nci_rf_disc_map_rsp)); + + return (void *)0; +error: + return (void *)-1; +} + +FIXTURE_SETUP(NCI) +{ + struct msgtemplate msg; + pthread_t thread_t; + __u32 event_group; + int status; + int rc; + + self->open_state = false; + self->proto = VIRTUAL_NFC_PROTOCOLS; + self->isNCI2 = variant->isNCI2; + + self->sd = create_nl_socket(); + ASSERT_NE(self->sd, -1); + + self->pid = getpid(); + self->fid = get_family_id(self->sd, self->pid, &event_group); + ASSERT_NE(self->fid, -1); + + self->virtual_nci_fd = open("/dev/virtual_nci", O_RDWR); + ASSERT_GT(self->virtual_nci_fd, -1); + + rc = setsockopt(self->sd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &event_group, + sizeof(event_group)); + ASSERT_NE(rc, -1); + + rc = ioctl(self->virtual_nci_fd, IOCTL_GET_NCIDEV_IDX, &self->dev_idex); + ASSERT_EQ(rc, 0); + + rc = get_nci_devid(self->sd, self->fid, self->pid, self->dev_idex, &msg); + ASSERT_EQ(rc, 0); + EXPECT_EQ(get_dev_enable_state(&msg), 0); + + if (self->isNCI2) + rc = pthread_create(&thread_t, NULL, virtual_dev_open_v2, + (void *)&self->virtual_nci_fd); + else + rc = pthread_create(&thread_t, NULL, virtual_dev_open, + (void *)&self->virtual_nci_fd); + ASSERT_GT(rc, -1); + + rc = send_cmd_with_idx(self->sd, self->fid, self->pid, + NFC_CMD_DEV_UP, self->dev_idex); + EXPECT_EQ(rc, 0); + + pthread_join(thread_t, (void **)&status); + ASSERT_EQ(status, 0); + self->open_state = true; +} + +static void *virtual_deinit(void *data) +{ + char buf[258]; + int dev_fd; + int len; + + dev_fd = *(int *)data; + + len = read(dev_fd, buf, 258); + if (len <= 0) + goto error; + if (len != sizeof(nci_reset_cmd)) + goto error; + if (memcmp(nci_reset_cmd, buf, len)) + goto error; + write(dev_fd, nci_reset_rsp, sizeof(nci_reset_rsp)); + + return (void *)0; +error: + return (void *)-1; +} + +static void *virtual_deinit_v2(void *data) +{ + char buf[258]; + int dev_fd; + int len; + + dev_fd = *(int *)data; + + len = read(dev_fd, buf, 258); + if (len <= 0) + goto error; + if (len != sizeof(nci_reset_cmd)) + goto error; + if (memcmp(nci_reset_cmd, buf, len)) + goto error; + write(dev_fd, nci_reset_rsp_v2, sizeof(nci_reset_rsp_v2)); + write(dev_fd, nci_reset_ntf, sizeof(nci_reset_ntf)); + + return (void *)0; +error: + return (void *)-1; +} + +FIXTURE_TEARDOWN(NCI) +{ + pthread_t thread_t; + int status; + int rc; + + if (self->open_state) { + if (self->isNCI2) + rc = pthread_create(&thread_t, NULL, + virtual_deinit_v2, + (void *)&self->virtual_nci_fd); + else + rc = pthread_create(&thread_t, NULL, virtual_deinit, + (void *)&self->virtual_nci_fd); + + ASSERT_GT(rc, -1); + rc = send_cmd_with_idx(self->sd, self->fid, self->pid, + NFC_CMD_DEV_DOWN, self->dev_idex); + EXPECT_EQ(rc, 0); + + pthread_join(thread_t, (void **)&status); + ASSERT_EQ(status, 0); + } + + close(self->sd); + close(self->virtual_nci_fd); + self->open_state = false; +} + +TEST_F(NCI, init) +{ + struct msgtemplate msg; + int rc; + + rc = get_nci_devid(self->sd, self->fid, self->pid, self->dev_idex, + &msg); + ASSERT_EQ(rc, 0); + EXPECT_EQ(get_dev_enable_state(&msg), 1); +} + +static void *virtual_poll_start(void *data) +{ + char buf[258]; + int dev_fd; + int len; + + dev_fd = *(int *)data; + + len = read(dev_fd, buf, 258); + if (len <= 0) + goto error; + if (len != sizeof(nci_rf_discovery_cmd)) + goto error; + if (memcmp(nci_rf_discovery_cmd, buf, len)) + goto error; + write(dev_fd, nci_rf_disc_rsp, sizeof(nci_rf_disc_rsp)); + + return (void *)0; +error: + return (void *)-1; +} + +static void *virtual_poll_stop(void *data) +{ + char buf[258]; + int dev_fd; + int len; + + dev_fd = *(int *)data; + + len = read(dev_fd, buf, 258); + if (len <= 0) + goto error; + if (len != sizeof(nci_rf_deact_cmd)) + goto error; + if (memcmp(nci_rf_deact_cmd, buf, len)) + goto error; + write(dev_fd, nci_rf_deact_rsp, sizeof(nci_rf_deact_rsp)); + + return (void *)0; +error: + return (void *)-1; +} + +int start_polling(int dev_idx, int proto, int virtual_fd, int sd, int fid, int pid) +{ + __u16 nla_start_poll_type[2] = {NFC_ATTR_DEVICE_INDEX, + NFC_ATTR_PROTOCOLS}; + void *nla_start_poll_data[2] = {&dev_idx, &proto}; + int nla_start_poll_len[2] = {4, 4}; + pthread_t thread_t; + int status; + int rc; + + rc = pthread_create(&thread_t, NULL, virtual_poll_start, + (void *)&virtual_fd); + if (rc < 0) + return rc; + + rc = send_cmd_mt_nla(sd, fid, pid, NFC_CMD_START_POLL, 2, nla_start_poll_type, + nla_start_poll_data, nla_start_poll_len, NLM_F_REQUEST); + if (rc != 0) + return rc; + + pthread_join(thread_t, (void **)&status); + return status; +} + +int stop_polling(int dev_idx, int virtual_fd, int sd, int fid, int pid) +{ + pthread_t thread_t; + int status; + int rc; + + rc = pthread_create(&thread_t, NULL, virtual_poll_stop, + (void *)&virtual_fd); + if (rc < 0) + return rc; + + rc = send_cmd_with_idx(sd, fid, pid, + NFC_CMD_STOP_POLL, dev_idx); + if (rc != 0) + return rc; + + pthread_join(thread_t, (void **)&status); + return status; +} + +TEST_F(NCI, start_poll) +{ + int status; + + status = start_polling(self->dev_idex, self->proto, self->virtual_nci_fd, + self->sd, self->fid, self->pid); + EXPECT_EQ(status, 0); + + status = stop_polling(self->dev_idex, self->virtual_nci_fd, self->sd, + self->fid, self->pid); + EXPECT_EQ(status, 0); +} + +int get_taginfo(int dev_idx, int sd, int fid, int pid) +{ + struct { + struct nlmsghdr n; + struct genlmsghdr g; + char buf[512]; + } ans; + + struct nlattr *na; + __u32 protocol; + int targetidx; + __u8 sel_res; + int resp_len; + int len; + + __u16 tagid_type; + void *tagid_type_data; + int tagid_len; + + tagid_type = NFC_ATTR_DEVICE_INDEX; + tagid_type_data = &dev_idx; + tagid_len = 4; + + send_cmd_mt_nla(sd, fid, pid, NFC_CMD_GET_TARGET, 1, &tagid_type, + &tagid_type_data, &tagid_len, NLM_F_REQUEST | NLM_F_DUMP); + resp_len = recv(sd, &ans, sizeof(ans), 0); + if (ans.n.nlmsg_type == NLMSG_ERROR || resp_len < 0 || + !NLMSG_OK(&ans.n, resp_len)) + return -1; + + resp_len = GENLMSG_PAYLOAD(&ans.n); + na = (struct nlattr *)GENLMSG_DATA(&ans); + + len = 0; + targetidx = -1; + protocol = -1; + sel_res = -1; + + while (len < resp_len) { + len += NLA_ALIGN(na->nla_len); + + if (na->nla_type == NFC_ATTR_TARGET_INDEX) + targetidx = *(int *)((char *)na + NLA_HDRLEN); + else if (na->nla_type == NFC_ATTR_TARGET_SEL_RES) + sel_res = *(__u8 *)((char *)na + NLA_HDRLEN); + else if (na->nla_type == NFC_ATTR_PROTOCOLS) + protocol = *(__u32 *)((char *)na + NLA_HDRLEN); + + na = (struct nlattr *)(GENLMSG_DATA(&ans) + len); + } + + if (targetidx == -1 || sel_res != 0x20 || protocol != NFC_PROTO_ISO14443_MASK) + return -1; + + return targetidx; +} + +int connect_socket(int dev_idx, int target_idx) +{ + struct sockaddr_nfc addr; + int sock; + int err = 0; + + sock = socket(AF_NFC, SOCK_SEQPACKET, NFC_SOCKPROTO_RAW); + if (sock == -1) + return -1; + + addr.sa_family = AF_NFC; + addr.dev_idx = dev_idx; + addr.target_idx = target_idx; + addr.nfc_protocol = NFC_PROTO_ISO14443; + + err = connect(sock, (struct sockaddr *)&addr, sizeof(addr)); + if (err) { + close(sock); + return -1; + } + + return sock; +} + +int connect_tag(int dev_idx, int virtual_fd, int sd, int fid, int pid) +{ + struct genlmsghdr *genlhdr; + struct nlattr *na; + char evt_data[255]; + int target_idx; + int resp_len; + int evt_dev; + + write(virtual_fd, nci_rf_activate_ntf, sizeof(nci_rf_activate_ntf)); + resp_len = recv(sd, evt_data, sizeof(evt_data), 0); + if (resp_len < 0) + return -1; + + genlhdr = (struct genlmsghdr *)((struct nlmsghdr *)evt_data + 1); + na = (struct nlattr *)(genlhdr + 1); + evt_dev = *(int *)((char *)na + NLA_HDRLEN); + if (dev_idx != evt_dev) + return -1; + + target_idx = get_taginfo(dev_idx, sd, fid, pid); + if (target_idx == -1) + return -1; + return connect_socket(dev_idx, target_idx); +} + +int read_write_nci_cmd(int nfc_sock, int virtual_fd, const __u8 *cmd, __u32 cmd_len, + const __u8 *rsp, __u32 rsp_len) +{ + char buf[256]; + int len; + + send(nfc_sock, &cmd[3], cmd_len - 3, 0); + len = read(virtual_fd, buf, cmd_len); + if (len < 0 || memcmp(buf, cmd, cmd_len)) + return -1; + + write(virtual_fd, rsp, rsp_len); + len = recv(nfc_sock, buf, rsp_len - 2, 0); + if (len < 0 || memcmp(&buf[1], &rsp[3], rsp_len - 3)) + return -1; + + return 0; +} + +int read_tag(int nfc_sock, int virtual_fd) +{ + if (read_write_nci_cmd(nfc_sock, virtual_fd, nci_t4t_select_cmd, + sizeof(nci_t4t_select_cmd), nci_t4t_rsp_ok, + sizeof(nci_t4t_rsp_ok))) + return -1; + + if (read_write_nci_cmd(nfc_sock, virtual_fd, nci_t4t_select_cmd2, + sizeof(nci_t4t_select_cmd2), nci_t4t_rsp_ok, + sizeof(nci_t4t_rsp_ok))) + return -1; + + if (read_write_nci_cmd(nfc_sock, virtual_fd, nci_t4t_read_cmd, + sizeof(nci_t4t_read_cmd), nci_t4t_read_rsp, + sizeof(nci_t4t_read_rsp))) + return -1; + + if (read_write_nci_cmd(nfc_sock, virtual_fd, nci_t4t_select_cmd3, + sizeof(nci_t4t_select_cmd3), nci_t4t_rsp_ok, + sizeof(nci_t4t_rsp_ok))) + return -1; + + if (read_write_nci_cmd(nfc_sock, virtual_fd, nci_t4t_read_cmd2, + sizeof(nci_t4t_read_cmd2), nci_t4t_read_rsp2, + sizeof(nci_t4t_read_rsp2))) + return -1; + + return read_write_nci_cmd(nfc_sock, virtual_fd, nci_t4t_read_cmd3, + sizeof(nci_t4t_read_cmd3), nci_t4t_read_rsp3, + sizeof(nci_t4t_read_rsp3)); +} + +static void *virtual_deactivate_proc(void *data) +{ + int virtual_fd; + char buf[256]; + int deactcmd_len; + int len; + + virtual_fd = *(int *)data; + deactcmd_len = sizeof(nci_rf_deact_cmd); + len = read(virtual_fd, buf, deactcmd_len); + if (len != deactcmd_len || memcmp(buf, nci_rf_deact_cmd, deactcmd_len)) + return (void *)-1; + + write(virtual_fd, nci_rf_deact_rsp, sizeof(nci_rf_deact_rsp)); + write(virtual_fd, nci_rf_deact_ntf, sizeof(nci_rf_deact_ntf)); + + return (void *)0; +} + +int disconnect_tag(int nfc_sock, int virtual_fd) +{ + pthread_t thread_t; + char buf[256]; + int status; + int len; + + send(nfc_sock, &nci_t4t_select_cmd3[3], sizeof(nci_t4t_select_cmd3) - 3, 0); + len = read(virtual_fd, buf, sizeof(nci_t4t_select_cmd3)); + if (len < 0 || memcmp(buf, nci_t4t_select_cmd3, sizeof(nci_t4t_select_cmd3))) + return -1; + + len = recv(nfc_sock, buf, sizeof(nci_t4t_rsp_ok), 0); + if (len != -1) + return -1; + + status = pthread_create(&thread_t, NULL, virtual_deactivate_proc, + (void *)&virtual_fd); + + close(nfc_sock); + pthread_join(thread_t, (void **)&status); + return status; +} + +TEST_F(NCI, t4t_tag_read) +{ + int nfc_sock; + int status; + + status = start_polling(self->dev_idex, self->proto, self->virtual_nci_fd, + self->sd, self->fid, self->pid); + EXPECT_EQ(status, 0); + + nfc_sock = connect_tag(self->dev_idex, self->virtual_nci_fd, self->sd, + self->fid, self->pid); + ASSERT_GT(nfc_sock, -1); + + status = read_tag(nfc_sock, self->virtual_nci_fd); + ASSERT_EQ(status, 0); + + status = disconnect_tag(nfc_sock, self->virtual_nci_fd); + EXPECT_EQ(status, 0); +} + +TEST_F(NCI, deinit) +{ + struct msgtemplate msg; + pthread_t thread_t; + int status; + int rc; + + rc = get_nci_devid(self->sd, self->fid, self->pid, self->dev_idex, + &msg); + ASSERT_EQ(rc, 0); + EXPECT_EQ(get_dev_enable_state(&msg), 1); + + if (self->isNCI2) + rc = pthread_create(&thread_t, NULL, virtual_deinit_v2, + (void *)&self->virtual_nci_fd); + else + rc = pthread_create(&thread_t, NULL, virtual_deinit, + (void *)&self->virtual_nci_fd); + ASSERT_GT(rc, -1); + + rc = send_cmd_with_idx(self->sd, self->fid, self->pid, + NFC_CMD_DEV_DOWN, self->dev_idex); + EXPECT_EQ(rc, 0); + + pthread_join(thread_t, (void **)&status); + self->open_state = 0; + ASSERT_EQ(status, 0); + + rc = get_nci_devid(self->sd, self->fid, self->pid, self->dev_idex, + &msg); + ASSERT_EQ(rc, 0); + EXPECT_EQ(get_dev_enable_state(&msg), 0); + + /* Test that operations that normally send packets to the driver + * don't cause issues when the device is already closed. + * Note: the send of NFC_CMD_DEV_UP itself still succeeds it's just + * that the device won't actually be up. + */ + close(self->virtual_nci_fd); + self->virtual_nci_fd = -1; + rc = send_cmd_with_idx(self->sd, self->fid, self->pid, + NFC_CMD_DEV_UP, self->dev_idex); + EXPECT_EQ(rc, 0); +} + +TEST_HARNESS_MAIN |