sdk-hwV1.3/lichee/linux-4.9/drivers/misc/sunxi-addr/sunxi-addr-mgt.c

398 lines
9.6 KiB
C
Raw Normal View History

2024-05-07 10:09:20 +00:00
/*
* The driver of SUNXI NET MAC ADDR Manager.
*
* Copyright (C) 2013 Allwinner.
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#define DEBUG
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/crypto.h>
#include <crypto/algapi.h>
#include <crypto/hash.h>
#include <linux/sunxi-sid.h>
#include <linux/miscdevice.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#define ADDR_MGT_DBG(fmt, arg...) pr_debug("[ADDR_MGT] %s: " fmt "\n",\
__func__, ## arg)
#define ADDR_MGT_ERR(fmt, arg...) pr_err("[ADDR_MGT] %s: " fmt "\n",\
__func__, ## arg)
#define BUFF_MAX 20
#define ID_LEN 16
#define HASH_LEN 16
#define TYPE_ANY 0
#define TYPE_BURN 1
#define TYPE_IDGEN 2
#define TYPE_USER 3
#define IS_TYPE_INVALID(x) ((x != TYPE_BURN) && \
(x != TYPE_IDGEN) && \
(x != TYPE_ANY) && \
(x != TYPE_USER))
#define DEF_TYPE_ADDR_WIFI TYPE_ANY
#define DEF_TYPE_ADDR_BT TYPE_ANY
#define DEF_TYPE_ADDR_ETH TYPE_ANY
struct addr_mgt_info {
unsigned int type_def_wifi;
unsigned int type_cur_wifi;
unsigned int type_def_bt;
unsigned int type_cur_bt;
unsigned int type_def_eth;
unsigned int type_cur_eth;
char addr_wifi[BUFF_MAX];
char addr_bt[BUFF_MAX];
char addr_eth[BUFF_MAX];
};
static struct addr_mgt_info info;
static int addr_parse(const char *addr_str, int check)
{
char addr[6];
char cmp_buf[6];
int ret = sscanf(addr_str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
&addr[0], &addr[1], &addr[2],
&addr[3], &addr[4], &addr[5]);
if (ret != 6)
return -1;
if (check && (addr[0] & 0x3))
return -1;
memset(cmp_buf, 0x00, 6);
if (memcmp(addr, cmp_buf, 6) == 0)
return -1;
memset(cmp_buf, 0xFF, 6);
if (memcmp(addr, cmp_buf, 6) == 0)
return -1;
return 0;
}
int get_wifi_custom_mac_address(char *addr_str)
{
if (IS_TYPE_INVALID(info.type_cur_wifi) ||
addr_parse(info.addr_wifi, 1))
return -1;
strcpy(addr_str, info.addr_wifi);
return info.type_cur_wifi;
}
EXPORT_SYMBOL_GPL(get_wifi_custom_mac_address);
int get_bt_custom_mac_address(char *addr_str)
{
if (IS_TYPE_INVALID(info.type_cur_bt) ||
addr_parse(info.addr_bt, 0))
return -1;
strcpy(addr_str, info.addr_bt);
return info.type_cur_bt;
}
EXPORT_SYMBOL_GPL(get_bt_custom_mac_address);
int get_eth_custom_mac_address(char *addr_str)
{
if (IS_TYPE_INVALID(info.type_cur_eth) ||
addr_parse(info.addr_eth, 1))
return -1;
strcpy(addr_str, info.addr_eth);
return info.type_cur_eth;
}
EXPORT_SYMBOL_GPL(get_eth_custom_mac_address);
static ssize_t addr_type_show(struct class *class,
struct class_attribute *attr, char *buffer)
{
return sprintf(buffer,
"TYPE_DEF_WIFI: %d, TYPE_DEF_BT: %d, TYPE_DEF_ETH: %d\n"
"TYPE_CUR_WIFI: %d, TYPE_CUR_BT: %d, TYPE_CUR_ETH: %d\n",
info.type_def_wifi, info.type_def_bt, info.type_def_eth,
info.type_cur_wifi, info.type_cur_bt, info.type_cur_eth);
}
static ssize_t addr_wifi_show(struct class *class,
struct class_attribute *attr, char *buffer)
{
if (IS_TYPE_INVALID(info.type_cur_wifi) ||
addr_parse(info.addr_wifi, 1)) {
ADDR_MGT_ERR("Wrong addr type or addr value.");
return 0;
}
return sprintf(buffer, "%.17s\n", info.addr_wifi);
}
static ssize_t addr_bt_show(struct class *class,
struct class_attribute *attr, char *buffer)
{
if (IS_TYPE_INVALID(info.type_cur_bt) ||
addr_parse(info.addr_bt, 0)) {
ADDR_MGT_ERR("Wrong addr type or addr value.");
return 0;
}
return sprintf(buffer, "%.17s\n", info.addr_bt);
}
static ssize_t addr_eth_show(struct class *class,
struct class_attribute *attr, char *buffer)
{
if (IS_TYPE_INVALID(info.type_cur_eth) ||
addr_parse(info.addr_eth, 1)) {
ADDR_MGT_ERR("Wrong addr type or addr value.");
return 0;
}
return sprintf(buffer, "%.17s\n", info.addr_eth);
}
static ssize_t addr_wifi_store(struct class *class,
struct class_attribute *attr,
const char *buffer, size_t count)
{
if ((count > BUFF_MAX) || addr_parse(buffer, 1)) {
ADDR_MGT_ERR("Format wrong.");
return -EINVAL;
}
info.type_cur_wifi = TYPE_USER;
return sprintf(info.addr_wifi, "%s", buffer);
}
static ssize_t addr_bt_store(struct class *class,
struct class_attribute *attr,
const char *buffer, size_t count)
{
if ((count > BUFF_MAX) || addr_parse(buffer, 0)) {
ADDR_MGT_ERR("Format wrong.");
return -EINVAL;
}
info.type_cur_bt = TYPE_USER;
return sprintf(info.addr_bt, "%s", buffer);
}
static ssize_t addr_eth_store(struct class *class,
struct class_attribute *attr,
const char *buffer, size_t count)
{
if ((count > BUFF_MAX) || addr_parse(buffer, 1)) {
ADDR_MGT_ERR("Format wrong.");
return -EINVAL;
}
info.type_cur_eth = TYPE_USER;
return sprintf(info.addr_eth, "%s", buffer);
}
static int __init setup_addr_wifi(char *str)
{
if (str != NULL)
strlcpy(info.addr_wifi, str, 18);
return 0;
}
__setup("wifi_mac=", setup_addr_wifi);
static int __init setup_addr_bt(char *str)
{
if (str != NULL)
strlcpy(info.addr_bt, str, 18);
return 0;
}
__setup("bt_mac=", setup_addr_bt);
static int __init setup_addr_eth(char *str)
{
if (str != NULL)
strlcpy(info.addr_eth, str, 18);
return 0;
}
__setup("mac_addr=", setup_addr_eth);
static int hash_md5(char *buffer, ssize_t size, char *result)
{
struct crypto_ahash *tfm;
struct ahash_request *req;
struct scatterlist sg;
memset(result, 0, ID_LEN);
tfm = crypto_alloc_ahash("md5", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm)) {
ADDR_MGT_ERR("Failed to alloc md5\n");
return -1;
}
req = ahash_request_alloc(tfm, GFP_KERNEL);
if (!req)
goto out;
ahash_request_set_callback(req, 0, NULL, NULL);
if (crypto_ahash_init(req)) {
ADDR_MGT_ERR("crypto_ahash_init() failed\n");
goto out_req;
}
sg_init_one(&sg, buffer, size);
ahash_request_set_crypt(req, &sg, result, size);
if (crypto_ahash_update(req)) {
ADDR_MGT_ERR("crypto_ahash_update() failed for id\n");
goto out_req;
}
if (crypto_ahash_final(req)) {
ADDR_MGT_ERR("crypto_ahash_final() failed for result\n");
goto out_req;
}
ahash_request_free(req);
crypto_free_ahash(tfm);
return 0;
out_req:
ahash_request_free(req);
out:
crypto_free_ahash(tfm);
return -1;
}
static int addr_init(void)
{
char id_buf[ID_LEN];
char hash_buf[HASH_LEN];
if (sunxi_get_soc_chipid(id_buf))
return -1;
if (hash_md5(id_buf, ID_LEN, hash_buf))
return -1;
if ((info.type_cur_wifi == TYPE_IDGEN) ||
((info.type_cur_wifi == TYPE_ANY) &&
addr_parse(info.addr_wifi, 1))) {
info.type_cur_wifi = TYPE_IDGEN;
hash_buf[0] &= 0xFC;
memset(info.addr_wifi, 0x0, BUFF_MAX);
sprintf(info.addr_wifi, "%02X:%02X:%02X:%02X:%02X:%02X",
hash_buf[0], hash_buf[1], hash_buf[2],
hash_buf[3], hash_buf[4], hash_buf[5]);
}
memcpy(&id_buf[0], &hash_buf[0], HASH_LEN);
hash_md5(id_buf, ID_LEN, hash_buf);
if ((info.type_cur_bt == TYPE_IDGEN) ||
((info.type_cur_bt == TYPE_ANY) &&
addr_parse(info.addr_bt, 0))) {
info.type_cur_bt = TYPE_IDGEN;
memset(info.addr_bt, 0x0, BUFF_MAX);
sprintf(info.addr_bt, "%02X:%02X:%02X:%02X:%02X:%02X",
hash_buf[0], hash_buf[1], hash_buf[2],
hash_buf[3], hash_buf[4], hash_buf[5]);
}
memcpy(&id_buf[0], &hash_buf[0], HASH_LEN);
hash_md5(id_buf, ID_LEN, hash_buf);
if ((info.type_cur_eth == TYPE_IDGEN) ||
((info.type_cur_eth == TYPE_ANY) &&
addr_parse(info.addr_eth, 1))) {
info.type_cur_eth = TYPE_IDGEN;
hash_buf[0] &= 0xFC;
memset(info.addr_eth, 0x0, BUFF_MAX);
sprintf(info.addr_eth, "%02X:%02X:%02X:%02X:%02X:%02X",
hash_buf[0], hash_buf[1], hash_buf[2],
hash_buf[3], hash_buf[4], hash_buf[5]);
}
return 0;
}
static struct class_attribute addr_class_attrs[] = {
__ATTR(addr_type, 0444, addr_type_show, NULL),
__ATTR(addr_wifi, 0644, addr_wifi_show, addr_wifi_store),
__ATTR(addr_bt, 0644, addr_bt_show, addr_bt_store),
__ATTR(addr_eth, 0644, addr_eth_show, addr_eth_store),
__ATTR_NULL,
};
static struct class addr_class = {
.name = "addr_mgt",
.owner = THIS_MODULE,
.class_attrs = addr_class_attrs,
};
static const struct of_device_id addr_mgt_ids[] = {
{ .compatible = "allwinner,sunxi-addr_mgt" },
{ /* Sentinel */ }
};
static int addr_mgt_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
int status;
info.type_def_wifi = DEF_TYPE_ADDR_WIFI;
info.type_def_bt = DEF_TYPE_ADDR_BT;
info.type_def_eth = DEF_TYPE_ADDR_ETH;
if (of_property_read_u32(np, "type_addr_wifi", &info.type_def_wifi))
ADDR_MGT_DBG("Failed to get type_def_wifi, use default: %d",
DEF_TYPE_ADDR_WIFI);
if (of_property_read_u32(np, "type_addr_bt", &info.type_def_bt))
ADDR_MGT_DBG("Failed to get type_def_bt, use default: %d",
DEF_TYPE_ADDR_BT);
if (of_property_read_u32(np, "type_addr_eth", &info.type_def_eth))
ADDR_MGT_DBG("Failed to get type_def_eth, use default: %d",
DEF_TYPE_ADDR_ETH);
info.type_cur_wifi = info.type_def_wifi;
info.type_cur_bt = info.type_def_bt;
info.type_cur_eth = info.type_def_eth;
status = class_register(&addr_class);
if (status < 0) {
ADDR_MGT_ERR("class register error, status: %d.", status);
return -1;
}
ADDR_MGT_DBG("success.");
addr_init();
return 0;
}
static int addr_mgt_remove(struct platform_device *pdev)
{
class_unregister(&addr_class);
return 0;
}
static struct platform_driver addr_mgt_driver = {
.probe = addr_mgt_probe,
.remove = addr_mgt_remove,
.driver = {
.owner = THIS_MODULE,
.name = "sunxi-addr-mgt",
.of_match_table = addr_mgt_ids,
},
};
module_platform_driver(addr_mgt_driver);
MODULE_AUTHOR("Allwinnertech");
MODULE_DESCRIPTION("Network MAC Addess Manager");
MODULE_LICENSE("GPL");