Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 720d3f16 authored by John W. Linville's avatar John W. Linville
Browse files


Samuel Ortiz <sameo@linux.intel.com> says:

This is the first NFC pull request for 3.10.

The 2 features we have with this one are:

- An LLCP Service Name Lookup (SNL) netlink interface for querying LLCP
  service availability from user space.
  Along the way, Thierry also improved the existing SNL interface for
  aggregating SNL responses.

- An initial LLCP socket options implementation, for setting the Receive
  Window (RW) and the Maximum Information Unit Extension (MIUX) per socket.
  This is need for the LLCP validation tests.

We also have a microread MEI build failure here: I am not sending this one to
3.9 because the MEI bus code is not there yet, so it won't break for anyone
else than me.

Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parents c678fb2a 40213fa8
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@ struct mei_nfc_hdr {
#define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD)

struct microread_mei_phy {
	struct mei_device *mei_device;
	struct mei_device *device;
	struct nfc_hci_dev *hdev;

	int powered;
+1 −0
Original line number Diff line number Diff line
@@ -298,6 +298,7 @@ struct ucred {
#define SOL_IUCV	277
#define SOL_CAIF	278
#define SOL_ALG		279
#define SOL_NFC		280

/* IPX options */
#define IPX_TYPE	1
+16 −0
Original line number Diff line number Diff line
@@ -90,6 +90,8 @@ enum nfc_commands {
	NFC_CMD_LLC_SET_PARAMS,
	NFC_CMD_ENABLE_SE,
	NFC_CMD_DISABLE_SE,
	NFC_CMD_LLC_SDREQ,
	NFC_EVENT_LLC_SDRES,
/* private: internal use only */
	__NFC_CMD_AFTER_LAST
};
@@ -140,11 +142,21 @@ enum nfc_attrs {
	NFC_ATTR_LLC_PARAM_RW,
	NFC_ATTR_LLC_PARAM_MIUX,
	NFC_ATTR_SE,
	NFC_ATTR_LLC_SDP,
/* private: internal use only */
	__NFC_ATTR_AFTER_LAST
};
#define NFC_ATTR_MAX (__NFC_ATTR_AFTER_LAST - 1)

enum nfc_sdp_attr {
	NFC_SDP_ATTR_UNSPEC,
	NFC_SDP_ATTR_URI,
	NFC_SDP_ATTR_SAP,
/* private: internal use only */
	__NFC_SDP_ATTR_AFTER_LAST
};
#define NFC_SDP_ATTR_MAX (__NFC_SDP_ATTR_AFTER_LAST - 1)

#define NFC_DEVICE_NAME_MAXSIZE 8
#define NFC_NFCID1_MAXSIZE 10
#define NFC_SENSB_RES_MAXSIZE 12
@@ -220,4 +232,8 @@ struct sockaddr_nfc_llcp {
#define NFC_LLCP_DIRECTION_RX		0x00
#define NFC_LLCP_DIRECTION_TX		0x01

/* socket option names */
#define NFC_LLCP_RW   0
#define NFC_LLCP_MIUX 1

#endif /*__LINUX_NFC_H */
+169 −36
Original line number Diff line number Diff line
@@ -117,6 +117,88 @@ u8 *nfc_llcp_build_tlv(u8 type, u8 *value, u8 value_length, u8 *tlv_length)
	return tlv;
}

struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdres_tlv(u8 tid, u8 sap)
{
	struct nfc_llcp_sdp_tlv *sdres;
	u8 value[2];

	sdres = kzalloc(sizeof(struct nfc_llcp_sdp_tlv), GFP_KERNEL);
	if (sdres == NULL)
		return NULL;

	value[0] = tid;
	value[1] = sap;

	sdres->tlv = nfc_llcp_build_tlv(LLCP_TLV_SDRES, value, 2,
					&sdres->tlv_len);
	if (sdres->tlv == NULL) {
		kfree(sdres);
		return NULL;
	}

	sdres->tid = tid;
	sdres->sap = sap;

	INIT_HLIST_NODE(&sdres->node);

	return sdres;
}

struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, char *uri,
						  size_t uri_len)
{
	struct nfc_llcp_sdp_tlv *sdreq;

	pr_debug("uri: %s, len: %zu\n", uri, uri_len);

	sdreq = kzalloc(sizeof(struct nfc_llcp_sdp_tlv), GFP_KERNEL);
	if (sdreq == NULL)
		return NULL;

	sdreq->tlv_len = uri_len + 3;

	if (uri[uri_len - 1] == 0)
		sdreq->tlv_len--;

	sdreq->tlv = kzalloc(sdreq->tlv_len + 1, GFP_KERNEL);
	if (sdreq->tlv == NULL) {
		kfree(sdreq);
		return NULL;
	}

	sdreq->tlv[0] = LLCP_TLV_SDREQ;
	sdreq->tlv[1] = sdreq->tlv_len - 2;
	sdreq->tlv[2] = tid;

	sdreq->tid = tid;
	sdreq->uri = sdreq->tlv + 3;
	memcpy(sdreq->uri, uri, uri_len);

	sdreq->time = jiffies;

	INIT_HLIST_NODE(&sdreq->node);

	return sdreq;
}

void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp)
{
	kfree(sdp->tlv);
	kfree(sdp);
}

void nfc_llcp_free_sdp_tlv_list(struct hlist_head *head)
{
	struct nfc_llcp_sdp_tlv *sdp;
	struct hlist_node *n;

	hlist_for_each_entry_safe(sdp, n, head, node) {
		hlist_del(&sdp->node);

		nfc_llcp_free_sdp_tlv(sdp);
	}
}

int nfc_llcp_parse_gb_tlv(struct nfc_llcp_local *local,
			  u8 *tlv_array, u16 tlv_array_len)
{
@@ -184,10 +266,10 @@ int nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock,

		switch (type) {
		case LLCP_TLV_MIUX:
			sock->miu = llcp_tlv_miux(tlv) + 128;
			sock->remote_miu = llcp_tlv_miux(tlv) + 128;
			break;
		case LLCP_TLV_RW:
			sock->rw = llcp_tlv_rw(tlv);
			sock->remote_rw = llcp_tlv_rw(tlv);
			break;
		case LLCP_TLV_SN:
			break;
@@ -200,7 +282,8 @@ int nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock,
		tlv += length + 2;
	}

	pr_debug("sock %p rw %d miu %d\n", sock, sock->rw, sock->miu);
	pr_debug("sock %p rw %d miu %d\n", sock,
		 sock->remote_rw, sock->remote_miu);

	return 0;
}
@@ -318,9 +401,9 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock)
	struct sk_buff *skb;
	u8 *service_name_tlv = NULL, service_name_tlv_length;
	u8 *miux_tlv = NULL, miux_tlv_length;
	u8 *rw_tlv = NULL, rw_tlv_length;
	u8 *rw_tlv = NULL, rw_tlv_length, rw;
	int err;
	u16 size = 0;
	u16 size = 0, miux;

	pr_debug("Sending CONNECT\n");

@@ -336,11 +419,15 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock)
		size += service_name_tlv_length;
	}

	miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0,
	/* If the socket parameters are not set, use the local ones */
	miux = sock->miux > LLCP_MAX_MIUX ? local->miux : sock->miux;
	rw = sock->rw > LLCP_MAX_RW ? local->rw : sock->rw;

	miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0,
				      &miux_tlv_length);
	size += miux_tlv_length;

	rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &local->rw, 0, &rw_tlv_length);
	rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length);
	size += rw_tlv_length;

	pr_debug("SKB size %d SN length %zu\n", size, sock->service_name_len);
@@ -377,9 +464,9 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock)
	struct nfc_llcp_local *local;
	struct sk_buff *skb;
	u8 *miux_tlv = NULL, miux_tlv_length;
	u8 *rw_tlv = NULL, rw_tlv_length;
	u8 *rw_tlv = NULL, rw_tlv_length, rw;
	int err;
	u16 size = 0;
	u16 size = 0, miux;

	pr_debug("Sending CC\n");

@@ -387,11 +474,15 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock)
	if (local == NULL)
		return -ENODEV;

	miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0,
	/* If the socket parameters are not set, use the local ones */
	miux = sock->miux > LLCP_MAX_MIUX ? local->miux : sock->miux;
	rw = sock->rw > LLCP_MAX_RW ? local->rw : sock->rw;

	miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0,
				      &miux_tlv_length);
	size += miux_tlv_length;

	rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &local->rw, 0, &rw_tlv_length);
	rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length);
	size += rw_tlv_length;

	skb = llcp_allocate_pdu(sock, LLCP_PDU_CC, size);
@@ -416,48 +507,90 @@ error_tlv:
	return err;
}

int nfc_llcp_send_snl(struct nfc_llcp_local *local, u8 tid, u8 sap)
static struct sk_buff *nfc_llcp_allocate_snl(struct nfc_llcp_local *local,
					     size_t tlv_length)
{
	struct sk_buff *skb;
	struct nfc_dev *dev;
	u8 *sdres_tlv = NULL, sdres_tlv_length, sdres[2];
	u16 size = 0;

	pr_debug("Sending SNL tid 0x%x sap 0x%x\n", tid, sap);

	if (local == NULL)
		return -ENODEV;
		return ERR_PTR(-ENODEV);

	dev = local->dev;
	if (dev == NULL)
		return -ENODEV;

	sdres[0] = tid;
	sdres[1] = sap;
	sdres_tlv = nfc_llcp_build_tlv(LLCP_TLV_SDRES, sdres, 0,
				       &sdres_tlv_length);
	if (sdres_tlv == NULL)
		return -ENOMEM;
		return ERR_PTR(-ENODEV);

	size += LLCP_HEADER_SIZE;
	size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE;
	size += sdres_tlv_length;
	size += tlv_length;

	skb = alloc_skb(size, GFP_KERNEL);
	if (skb == NULL) {
		kfree(sdres_tlv);
		return -ENOMEM;
	}
	if (skb == NULL)
		return ERR_PTR(-ENOMEM);

	skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE);

	skb = llcp_add_header(skb, LLCP_SAP_SDP, LLCP_SAP_SDP, LLCP_PDU_SNL);

	memcpy(skb_put(skb, sdres_tlv_length), sdres_tlv, sdres_tlv_length);
	return skb;
}

int nfc_llcp_send_snl_sdres(struct nfc_llcp_local *local,
			    struct hlist_head *tlv_list, size_t tlvs_len)
{
	struct nfc_llcp_sdp_tlv *sdp;
	struct hlist_node *n;
	struct sk_buff *skb;

	skb = nfc_llcp_allocate_snl(local, tlvs_len);
	if (IS_ERR(skb))
		return PTR_ERR(skb);

	hlist_for_each_entry_safe(sdp, n, tlv_list, node) {
		memcpy(skb_put(skb, sdp->tlv_len), sdp->tlv, sdp->tlv_len);

		hlist_del(&sdp->node);

		nfc_llcp_free_sdp_tlv(sdp);
	}

	skb_queue_tail(&local->tx_queue, skb);

	kfree(sdres_tlv);
	return 0;
}

int nfc_llcp_send_snl_sdreq(struct nfc_llcp_local *local,
			    struct hlist_head *tlv_list, size_t tlvs_len)
{
	struct nfc_llcp_sdp_tlv *sdreq;
	struct hlist_node *n;
	struct sk_buff *skb;

	skb = nfc_llcp_allocate_snl(local, tlvs_len);
	if (IS_ERR(skb))
		return PTR_ERR(skb);

	mutex_lock(&local->sdreq_lock);

	if (hlist_empty(&local->pending_sdreqs))
		mod_timer(&local->sdreq_timer,
			  jiffies + msecs_to_jiffies(3 * local->remote_lto));

	hlist_for_each_entry_safe(sdreq, n, tlv_list, node) {
		pr_debug("tid %d for %s\n", sdreq->tid, sdreq->uri);

		memcpy(skb_put(skb, sdreq->tlv_len), sdreq->tlv,
		       sdreq->tlv_len);

		hlist_del(&sdreq->node);

		hlist_add_head(&sdreq->node, &local->pending_sdreqs);
	}

	mutex_unlock(&local->sdreq_lock);

	skb_queue_tail(&local->tx_queue, skb);

	return 0;
}
@@ -532,8 +665,8 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,

	/* Remote is ready but has not acknowledged our frames */
	if((sock->remote_ready &&
	    skb_queue_len(&sock->tx_pending_queue) >= sock->rw &&
	    skb_queue_len(&sock->tx_queue) >= 2 * sock->rw)) {
	    skb_queue_len(&sock->tx_pending_queue) >= sock->remote_rw &&
	    skb_queue_len(&sock->tx_queue) >= 2 * sock->remote_rw)) {
		pr_err("Pending queue is full %d frames\n",
		       skb_queue_len(&sock->tx_pending_queue));
		return -ENOBUFS;
@@ -541,7 +674,7 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,

	/* Remote is not ready and we've been queueing enough frames */
	if ((!sock->remote_ready &&
	     skb_queue_len(&sock->tx_queue) >= 2 * sock->rw)) {
	     skb_queue_len(&sock->tx_queue) >= 2 * sock->remote_rw)) {
		pr_err("Tx queue is full %d frames\n",
		       skb_queue_len(&sock->tx_queue));
		return -ENOBUFS;
@@ -561,7 +694,7 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,

	while (remaining_len > 0) {

		frag_len = min_t(size_t, sock->miu, remaining_len);
		frag_len = min_t(size_t, sock->remote_miu, remaining_len);

		pr_debug("Fragment %zd bytes remaining %zd",
			 frag_len, remaining_len);
@@ -621,7 +754,7 @@ int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap,

	while (remaining_len > 0) {

		frag_len = min_t(size_t, sock->miu, remaining_len);
		frag_len = min_t(size_t, sock->remote_miu, remaining_len);

		pr_debug("Fragment %zd bytes remaining %zd",
			 frag_len, remaining_len);
+102 −10
Original line number Diff line number Diff line
@@ -156,6 +156,9 @@ static void local_release(struct kref *ref)
	cancel_work_sync(&local->rx_work);
	cancel_work_sync(&local->timeout_work);
	kfree_skb(local->rx_pending);
	del_timer_sync(&local->sdreq_timer);
	cancel_work_sync(&local->sdreq_timeout_work);
	nfc_llcp_free_sdp_tlv_list(&local->pending_sdreqs);
	kfree(local);
}

@@ -223,6 +226,47 @@ static void nfc_llcp_symm_timer(unsigned long data)
	schedule_work(&local->timeout_work);
}

static void nfc_llcp_sdreq_timeout_work(struct work_struct *work)
{
	unsigned long time;
	HLIST_HEAD(nl_sdres_list);
	struct hlist_node *n;
	struct nfc_llcp_sdp_tlv *sdp;
	struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
						    sdreq_timeout_work);

	mutex_lock(&local->sdreq_lock);

	time = jiffies - msecs_to_jiffies(3 * local->remote_lto);

	hlist_for_each_entry_safe(sdp, n, &local->pending_sdreqs, node) {
		if (time_after(sdp->time, time))
			continue;

		sdp->sap = LLCP_SDP_UNBOUND;

		hlist_del(&sdp->node);

		hlist_add_head(&sdp->node, &nl_sdres_list);
	}

	if (!hlist_empty(&local->pending_sdreqs))
		mod_timer(&local->sdreq_timer,
			  jiffies + msecs_to_jiffies(3 * local->remote_lto));

	mutex_unlock(&local->sdreq_lock);

	if (!hlist_empty(&nl_sdres_list))
		nfc_genl_llc_send_sdres(local->dev, &nl_sdres_list);
}

static void nfc_llcp_sdreq_timer(unsigned long data)
{
	struct nfc_llcp_local *local = (struct nfc_llcp_local *) data;

	schedule_work(&local->sdreq_timeout_work);
}

struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev)
{
	struct nfc_llcp_local *local, *n;
@@ -766,8 +810,6 @@ static void nfc_llcp_recv_ui(struct nfc_llcp_local *local,
	ui_cb->dsap = dsap;
	ui_cb->ssap = ssap;

	printk("%s %d %d\n", __func__, dsap, ssap);

	pr_debug("%d %d\n", dsap, ssap);

	/* We're looking for a bound socket, not a client one */
@@ -865,7 +907,9 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,
	new_sock = nfc_llcp_sock(new_sk);
	new_sock->dev = local->dev;
	new_sock->local = nfc_llcp_local_get(local);
	new_sock->miu = local->remote_miu;
	new_sock->rw = sock->rw;
	new_sock->miux = sock->miux;
	new_sock->remote_miu = local->remote_miu;
	new_sock->nfc_protocol = sock->nfc_protocol;
	new_sock->dsap = ssap;
	new_sock->target_idx = local->target_idx;
@@ -919,11 +963,11 @@ int nfc_llcp_queue_i_frames(struct nfc_llcp_sock *sock)

	pr_debug("Remote ready %d tx queue len %d remote rw %d",
		 sock->remote_ready, skb_queue_len(&sock->tx_pending_queue),
		 sock->rw);
		 sock->remote_rw);

	/* Try to queue some I frames for transmission */
	while (sock->remote_ready &&
	       skb_queue_len(&sock->tx_pending_queue) < sock->rw) {
	       skb_queue_len(&sock->tx_pending_queue) < sock->remote_rw) {
		struct sk_buff *pdu;

		pdu = skb_dequeue(&sock->tx_queue);
@@ -1144,6 +1188,10 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
	u16 tlv_len, offset;
	char *service_name;
	size_t service_name_len;
	struct nfc_llcp_sdp_tlv *sdp;
	HLIST_HEAD(llc_sdres_list);
	size_t sdres_tlvs_len;
	HLIST_HEAD(nl_sdres_list);

	dsap = nfc_llcp_dsap(skb);
	ssap = nfc_llcp_ssap(skb);
@@ -1158,6 +1206,7 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
	tlv = &skb->data[LLCP_HEADER_SIZE];
	tlv_len = skb->len - LLCP_HEADER_SIZE;
	offset = 0;
	sdres_tlvs_len = 0;

	while (offset < tlv_len) {
		type = tlv[0];
@@ -1175,14 +1224,14 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
			    !strncmp(service_name, "urn:nfc:sn:sdp",
				     service_name_len)) {
				sap = 1;
				goto send_snl;
				goto add_snl;
			}

			llcp_sock = nfc_llcp_sock_from_sn(local, service_name,
							  service_name_len);
			if (!llcp_sock) {
				sap = 0;
				goto send_snl;
				goto add_snl;
			}

			/*
@@ -1199,7 +1248,7 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,

				if (sap == LLCP_SAP_MAX) {
					sap = 0;
					goto send_snl;
					goto add_snl;
				}

				client_count =
@@ -1216,8 +1265,37 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,

			pr_debug("%p %d\n", llcp_sock, sap);

send_snl:
			nfc_llcp_send_snl(local, tid, sap);
add_snl:
			sdp = nfc_llcp_build_sdres_tlv(tid, sap);
			if (sdp == NULL)
				goto exit;

			sdres_tlvs_len += sdp->tlv_len;
			hlist_add_head(&sdp->node, &llc_sdres_list);
			break;

		case LLCP_TLV_SDRES:
			mutex_lock(&local->sdreq_lock);

			pr_debug("LLCP_TLV_SDRES: searching tid %d\n", tlv[2]);

			hlist_for_each_entry(sdp, &local->pending_sdreqs, node) {
				if (sdp->tid != tlv[2])
					continue;

				sdp->sap = tlv[3];

				pr_debug("Found: uri=%s, sap=%d\n",
					 sdp->uri, sdp->sap);

				hlist_del(&sdp->node);

				hlist_add_head(&sdp->node, &nl_sdres_list);

				break;
			}

			mutex_unlock(&local->sdreq_lock);
			break;

		default:
@@ -1228,6 +1306,13 @@ send_snl:
		offset += length + 2;
		tlv += length + 2;
	}

exit:
	if (!hlist_empty(&nl_sdres_list))
		nfc_genl_llc_send_sdres(local->dev, &nl_sdres_list);

	if (!hlist_empty(&llc_sdres_list))
		nfc_llcp_send_snl_sdres(local, &llc_sdres_list, sdres_tlvs_len);
}

static void nfc_llcp_rx_work(struct work_struct *work)
@@ -1413,6 +1498,13 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
	local->remote_miu = LLCP_DEFAULT_MIU;
	local->remote_lto = LLCP_DEFAULT_LTO;

	mutex_init(&local->sdreq_lock);
	INIT_HLIST_HEAD(&local->pending_sdreqs);
	init_timer(&local->sdreq_timer);
	local->sdreq_timer.data = (unsigned long) local;
	local->sdreq_timer.function = nfc_llcp_sdreq_timer;
	INIT_WORK(&local->sdreq_timeout_work, nfc_llcp_sdreq_timeout_work);

	list_add(&local->list, &llcp_devices);

	return 0;
Loading