162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Link Layer Control manager
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012  Intel Corporation. All rights reserved.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <net/nfc/llc.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "llc.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic LIST_HEAD(llc_engines);
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ciint __init nfc_llc_init(void)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	int r;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	r = nfc_llc_nop_register();
1962306a36Sopenharmony_ci	if (r)
2062306a36Sopenharmony_ci		goto exit;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	r = nfc_llc_shdlc_register();
2362306a36Sopenharmony_ci	if (r)
2462306a36Sopenharmony_ci		goto exit;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	return 0;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ciexit:
2962306a36Sopenharmony_ci	nfc_llc_exit();
3062306a36Sopenharmony_ci	return r;
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_civoid nfc_llc_exit(void)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	struct nfc_llc_engine *llc_engine, *n;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	list_for_each_entry_safe(llc_engine, n, &llc_engines, entry) {
3862306a36Sopenharmony_ci		list_del(&llc_engine->entry);
3962306a36Sopenharmony_ci		kfree(llc_engine->name);
4062306a36Sopenharmony_ci		kfree(llc_engine);
4162306a36Sopenharmony_ci	}
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ciint nfc_llc_register(const char *name, const struct nfc_llc_ops *ops)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	struct nfc_llc_engine *llc_engine;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	llc_engine = kzalloc(sizeof(struct nfc_llc_engine), GFP_KERNEL);
4962306a36Sopenharmony_ci	if (llc_engine == NULL)
5062306a36Sopenharmony_ci		return -ENOMEM;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	llc_engine->name = kstrdup(name, GFP_KERNEL);
5362306a36Sopenharmony_ci	if (llc_engine->name == NULL) {
5462306a36Sopenharmony_ci		kfree(llc_engine);
5562306a36Sopenharmony_ci		return -ENOMEM;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci	llc_engine->ops = ops;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	INIT_LIST_HEAD(&llc_engine->entry);
6062306a36Sopenharmony_ci	list_add_tail(&llc_engine->entry, &llc_engines);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return 0;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic struct nfc_llc_engine *nfc_llc_name_to_engine(const char *name)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct nfc_llc_engine *llc_engine;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	list_for_each_entry(llc_engine, &llc_engines, entry) {
7062306a36Sopenharmony_ci		if (strcmp(llc_engine->name, name) == 0)
7162306a36Sopenharmony_ci			return llc_engine;
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	return NULL;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_civoid nfc_llc_unregister(const char *name)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct nfc_llc_engine *llc_engine;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	llc_engine = nfc_llc_name_to_engine(name);
8262306a36Sopenharmony_ci	if (llc_engine == NULL)
8362306a36Sopenharmony_ci		return;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	list_del(&llc_engine->entry);
8662306a36Sopenharmony_ci	kfree(llc_engine->name);
8762306a36Sopenharmony_ci	kfree(llc_engine);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistruct nfc_llc *nfc_llc_allocate(const char *name, struct nfc_hci_dev *hdev,
9162306a36Sopenharmony_ci				 xmit_to_drv_t xmit_to_drv,
9262306a36Sopenharmony_ci				 rcv_to_hci_t rcv_to_hci, int tx_headroom,
9362306a36Sopenharmony_ci				 int tx_tailroom, llc_failure_t llc_failure)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct nfc_llc_engine *llc_engine;
9662306a36Sopenharmony_ci	struct nfc_llc *llc;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	llc_engine = nfc_llc_name_to_engine(name);
9962306a36Sopenharmony_ci	if (llc_engine == NULL)
10062306a36Sopenharmony_ci		return NULL;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	llc = kzalloc(sizeof(struct nfc_llc), GFP_KERNEL);
10362306a36Sopenharmony_ci	if (llc == NULL)
10462306a36Sopenharmony_ci		return NULL;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	llc->data = llc_engine->ops->init(hdev, xmit_to_drv, rcv_to_hci,
10762306a36Sopenharmony_ci					  tx_headroom, tx_tailroom,
10862306a36Sopenharmony_ci					  &llc->rx_headroom, &llc->rx_tailroom,
10962306a36Sopenharmony_ci					  llc_failure);
11062306a36Sopenharmony_ci	if (llc->data == NULL) {
11162306a36Sopenharmony_ci		kfree(llc);
11262306a36Sopenharmony_ci		return NULL;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci	llc->ops = llc_engine->ops;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return llc;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_civoid nfc_llc_free(struct nfc_llc *llc)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	llc->ops->deinit(llc);
12262306a36Sopenharmony_ci	kfree(llc);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ciint nfc_llc_start(struct nfc_llc *llc)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	return llc->ops->start(llc);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ciEXPORT_SYMBOL(nfc_llc_start);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ciint nfc_llc_stop(struct nfc_llc *llc)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	return llc->ops->stop(llc);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ciEXPORT_SYMBOL(nfc_llc_stop);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_civoid nfc_llc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	llc->ops->rcv_from_drv(llc, skb);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ciint nfc_llc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	return llc->ops->xmit_from_hci(llc, skb);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_civoid *nfc_llc_get_data(struct nfc_llc *llc)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	return llc->data;
15062306a36Sopenharmony_ci}
151