WSL2-Linux-Kernel/drivers/uwb/address.c

353 строки
9.4 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Ultra Wide Band
* Address management
*
* Copyright (C) 2005-2006 Intel Corporation
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* FIXME: docs
*/
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/random.h>
#include <linux/etherdevice.h>
#include "uwb-internal.h"
/** Device Address Management command */
struct uwb_rc_cmd_dev_addr_mgmt {
struct uwb_rccb rccb;
u8 bmOperationType;
u8 baAddr[6];
} __attribute__((packed));
/**
* Low level command for setting/getting UWB radio's addresses
*
* @hwarc: HWA Radio Control interface instance
* @bmOperationType:
* Set/get, MAC/DEV (see WUSB1.0[8.6.2.2])
* @baAddr: address buffer--assumed to have enough data to hold
* the address type requested.
* @reply: Pointer to reply buffer (can be stack allocated)
* @returns: 0 if ok, < 0 errno code on error.
*
* @cmd has to be allocated because USB cannot grok USB or vmalloc
* buffers depending on your combination of host architecture.
*/
static
int uwb_rc_dev_addr_mgmt(struct uwb_rc *rc,
u8 bmOperationType, const u8 *baAddr,
struct uwb_rc_evt_dev_addr_mgmt *reply)
{
int result;
struct uwb_rc_cmd_dev_addr_mgmt *cmd;
result = -ENOMEM;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (cmd == NULL)
goto error_kzalloc;
cmd->rccb.bCommandType = UWB_RC_CET_GENERAL;
cmd->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_DEV_ADDR_MGMT);
cmd->bmOperationType = bmOperationType;
if (baAddr) {
size_t size = 0;
switch (bmOperationType >> 1) {
case 0: size = 2; break;
case 1: size = 6; break;
default: BUG();
}
memcpy(cmd->baAddr, baAddr, size);
}
reply->rceb.bEventType = UWB_RC_CET_GENERAL;
reply->rceb.wEvent = UWB_RC_CMD_DEV_ADDR_MGMT;
result = uwb_rc_cmd(rc, "DEV-ADDR-MGMT",
&cmd->rccb, sizeof(*cmd),
&reply->rceb, sizeof(*reply));
if (result < 0)
goto error_cmd;
if (result < sizeof(*reply)) {
dev_err(&rc->uwb_dev.dev,
"DEV-ADDR-MGMT: not enough data replied: "
"%d vs %zu bytes needed\n", result, sizeof(*reply));
result = -ENOMSG;
} else if (reply->bResultCode != UWB_RC_RES_SUCCESS) {
dev_err(&rc->uwb_dev.dev,
"DEV-ADDR-MGMT: command execution failed: %s (%d)\n",
uwb_rc_strerror(reply->bResultCode),
reply->bResultCode);
result = -EIO;
} else
result = 0;
error_cmd:
kfree(cmd);
error_kzalloc:
return result;
}
/**
* Set the UWB RC MAC or device address.
*
* @rc: UWB Radio Controller
* @_addr: Pointer to address to write [assumed to be either a
* 'struct uwb_mac_addr *' or a 'struct uwb_dev_addr *'].
* @type: Type of address to set (UWB_ADDR_DEV or UWB_ADDR_MAC).
* @returns: 0 if ok, < 0 errno code on error.
*
* Some anal retentivity here: even if both 'struct
* uwb_{dev,mac}_addr' have the actual byte array in the same offset
* and I could just pass _addr to hwarc_cmd_dev_addr_mgmt(), I prefer
* to use some syntatic sugar in case someday we decide to change the
* format of the structs. The compiler will optimize it out anyway.
*/
static int uwb_rc_addr_set(struct uwb_rc *rc,
const void *_addr, enum uwb_addr_type type)
{
int result;
u8 bmOperationType = 0x1; /* Set address */
const struct uwb_dev_addr *dev_addr = _addr;
const struct uwb_mac_addr *mac_addr = _addr;
struct uwb_rc_evt_dev_addr_mgmt reply;
const u8 *baAddr;
result = -EINVAL;
switch (type) {
case UWB_ADDR_DEV:
baAddr = dev_addr->data;
break;
case UWB_ADDR_MAC:
baAddr = mac_addr->data;
bmOperationType |= 0x2;
break;
default:
return result;
}
return uwb_rc_dev_addr_mgmt(rc, bmOperationType, baAddr, &reply);
}
/**
* Get the UWB radio's MAC or device address.
*
* @rc: UWB Radio Controller
* @_addr: Where to write the address data [assumed to be either a
* 'struct uwb_mac_addr *' or a 'struct uwb_dev_addr *'].
* @type: Type of address to get (UWB_ADDR_DEV or UWB_ADDR_MAC).
* @returns: 0 if ok (and *_addr set), < 0 errno code on error.
*
* See comment in uwb_rc_addr_set() about anal retentivity in the
* type handling of the address variables.
*/
static int uwb_rc_addr_get(struct uwb_rc *rc,
void *_addr, enum uwb_addr_type type)
{
int result;
u8 bmOperationType = 0x0; /* Get address */
struct uwb_rc_evt_dev_addr_mgmt evt;
struct uwb_dev_addr *dev_addr = _addr;
struct uwb_mac_addr *mac_addr = _addr;
u8 *baAddr;
result = -EINVAL;
switch (type) {
case UWB_ADDR_DEV:
baAddr = dev_addr->data;
break;
case UWB_ADDR_MAC:
bmOperationType |= 0x2;
baAddr = mac_addr->data;
break;
default:
return result;
}
result = uwb_rc_dev_addr_mgmt(rc, bmOperationType, baAddr, &evt);
if (result == 0)
switch (type) {
case UWB_ADDR_DEV:
memcpy(&dev_addr->data, evt.baAddr,
sizeof(dev_addr->data));
break;
case UWB_ADDR_MAC:
memcpy(&mac_addr->data, evt.baAddr,
sizeof(mac_addr->data));
break;
default: /* shut gcc up */
BUG();
}
return result;
}
/** Get @rc's MAC address to @addr */
int uwb_rc_mac_addr_get(struct uwb_rc *rc,
struct uwb_mac_addr *addr) {
return uwb_rc_addr_get(rc, addr, UWB_ADDR_MAC);
}
EXPORT_SYMBOL_GPL(uwb_rc_mac_addr_get);
/** Get @rc's device address to @addr */
int uwb_rc_dev_addr_get(struct uwb_rc *rc,
struct uwb_dev_addr *addr) {
return uwb_rc_addr_get(rc, addr, UWB_ADDR_DEV);
}
EXPORT_SYMBOL_GPL(uwb_rc_dev_addr_get);
/** Set @rc's address to @addr */
int uwb_rc_mac_addr_set(struct uwb_rc *rc,
const struct uwb_mac_addr *addr)
{
int result = -EINVAL;
mutex_lock(&rc->uwb_dev.mutex);
result = uwb_rc_addr_set(rc, addr, UWB_ADDR_MAC);
mutex_unlock(&rc->uwb_dev.mutex);
return result;
}
/** Set @rc's address to @addr */
int uwb_rc_dev_addr_set(struct uwb_rc *rc,
const struct uwb_dev_addr *addr)
{
int result = -EINVAL;
mutex_lock(&rc->uwb_dev.mutex);
result = uwb_rc_addr_set(rc, addr, UWB_ADDR_DEV);
rc->uwb_dev.dev_addr = *addr;
mutex_unlock(&rc->uwb_dev.mutex);
return result;
}
/* Returns !0 if given address is already assigned to device. */
int __uwb_mac_addr_assigned_check(struct device *dev, void *_addr)
{
struct uwb_dev *uwb_dev = to_uwb_dev(dev);
struct uwb_mac_addr *addr = _addr;
if (!uwb_mac_addr_cmp(addr, &uwb_dev->mac_addr))
return !0;
return 0;
}
/* Returns !0 if given address is already assigned to device. */
int __uwb_dev_addr_assigned_check(struct device *dev, void *_addr)
{
struct uwb_dev *uwb_dev = to_uwb_dev(dev);
struct uwb_dev_addr *addr = _addr;
if (!uwb_dev_addr_cmp(addr, &uwb_dev->dev_addr))
return !0;
return 0;
}
/**
* uwb_dev_addr_assign - assigned a generated DevAddr to a radio controller
* @rc: the (local) radio controller device requiring a new DevAddr
*
* A new DevAddr is required when:
* - first setting up a radio controller
* - if the hardware reports a DevAddr conflict
*
* The DevAddr is randomly generated in the generated DevAddr range
* [0x100, 0xfeff]. The number of devices in a beacon group is limited
* by mMaxBPLength (96) so this address space will never be exhausted.
*
* [ECMA-368] 17.1.1, 17.16.
*/
int uwb_rc_dev_addr_assign(struct uwb_rc *rc)
{
struct uwb_dev_addr new_addr;
do {
get_random_bytes(new_addr.data, sizeof(new_addr.data));
} while (new_addr.data[0] == 0x00 || new_addr.data[0] == 0xff
|| __uwb_dev_addr_assigned(rc, &new_addr));
return uwb_rc_dev_addr_set(rc, &new_addr);
}
/**
* uwbd_evt_handle_rc_dev_addr_conflict - handle a DEV_ADDR_CONFLICT event
* @evt: the DEV_ADDR_CONFLICT notification from the radio controller
*
* A new (non-conflicting) DevAddr is assigned to the radio controller.
*
* [ECMA-368] 17.1.1.1.
*/
int uwbd_evt_handle_rc_dev_addr_conflict(struct uwb_event *evt)
{
struct uwb_rc *rc = evt->rc;
return uwb_rc_dev_addr_assign(rc);
}
/*
* Print the 48-bit EUI MAC address of the radio controller when
* reading /sys/class/uwb_rc/XX/mac_address
*/
static ssize_t uwb_rc_mac_addr_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct uwb_dev *uwb_dev = to_uwb_dev(dev);
struct uwb_rc *rc = uwb_dev->rc;
struct uwb_mac_addr addr;
ssize_t result;
mutex_lock(&rc->uwb_dev.mutex);
result = uwb_rc_addr_get(rc, &addr, UWB_ADDR_MAC);
mutex_unlock(&rc->uwb_dev.mutex);
if (result >= 0) {
result = uwb_mac_addr_print(buf, UWB_ADDR_STRSIZE, &addr);
buf[result++] = '\n';
}
return result;
}
/*
* Parse a 48 bit address written to /sys/class/uwb_rc/XX/mac_address
* and if correct, set it.
*/
static ssize_t uwb_rc_mac_addr_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct uwb_dev *uwb_dev = to_uwb_dev(dev);
struct uwb_rc *rc = uwb_dev->rc;
struct uwb_mac_addr addr;
ssize_t result;
if (!mac_pton(buf, addr.data))
return -EINVAL;
if (is_multicast_ether_addr(addr.data)) {
dev_err(&rc->uwb_dev.dev, "refusing to set multicast "
"MAC address %s\n", buf);
return -EINVAL;
}
result = uwb_rc_mac_addr_set(rc, &addr);
if (result == 0)
rc->uwb_dev.mac_addr = addr;
return result < 0 ? result : size;
}
DEVICE_ATTR(mac_address, S_IRUGO | S_IWUSR, uwb_rc_mac_addr_show, uwb_rc_mac_addr_store);
/** Print @addr to @buf, @return bytes written */
size_t __uwb_addr_print(char *buf, size_t buf_size, const unsigned char *addr,
int type)
{
size_t result;
if (type)
result = scnprintf(buf, buf_size, "%pM", addr);
else
result = scnprintf(buf, buf_size, "%02x:%02x",
addr[1], addr[0]);
return result;
}
EXPORT_SYMBOL_GPL(__uwb_addr_print);