dect: add virtual transceiver driver
Add a driver for virtual DECT transceivers. Multiple transceivers can be joined together in a group, forming a virtual DECT network. Signal strength and interference are calculated based on the relative positions of the transceivers, which can be controlled through sysfs. Signed-off-by: Patrick McHardy <kaber@trash.net>
This commit is contained in:
parent
c06cee2d91
commit
e4ed1a3529
|
@ -5,6 +5,7 @@ menuconfig DECTDEVICES
|
|||
|
||||
if DECTDEVICES
|
||||
|
||||
source "drivers/dect/vtrx/Kconfig"
|
||||
source "drivers/dect/coa/Kconfig"
|
||||
|
||||
endif
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
obj-$(CONFIG_DECT_VTRX) += vtrx/
|
||||
obj-$(CONFIG_DECT_COA) += coa/
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
config DECT_VTRX
|
||||
tristate "DECT virtual transceiver support"
|
||||
depends on DECT
|
||||
help
|
||||
This option enables support for the virtual DECT transceiver.
|
|
@ -0,0 +1,2 @@
|
|||
obj-$(CONFIG_DECT_VTRX) += dect-vtrx.o
|
||||
dect-vtrx-objs := vtrx.o vtrx-sysfs.o mw_to_dbm.o
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* DECT virtual transceiver
|
||||
*
|
||||
* Copyright (c) 2010 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <net/dect/transceiver.h>
|
||||
#include "vtrx.h"
|
||||
|
||||
static const struct {
|
||||
u64 mw;
|
||||
int dbm;
|
||||
} mw_to_dbm_tbl[] = {
|
||||
{ 5ULL, -93 },
|
||||
{ 6ULL, -92 },
|
||||
{ 7ULL, -91 },
|
||||
{ 10ULL, -90 },
|
||||
{ 12ULL, -89 },
|
||||
{ 15ULL, -88 },
|
||||
{ 19ULL, -87 },
|
||||
{ 25ULL, -86 },
|
||||
{ 31ULL, -85 },
|
||||
{ 39ULL, -84 },
|
||||
{ 50ULL, -83 },
|
||||
{ 63ULL, -82 },
|
||||
{ 79ULL, -81 },
|
||||
{ 100ULL, -80 },
|
||||
{ 125ULL, -79 },
|
||||
{ 158ULL, -78 },
|
||||
{ 199ULL, -77 },
|
||||
{ 251ULL, -76 },
|
||||
{ 316ULL, -75 },
|
||||
{ 398ULL, -74 },
|
||||
{ 501ULL, -73 },
|
||||
{ 630ULL, -72 },
|
||||
{ 794ULL, -71 },
|
||||
{ 1000ULL, -70 },
|
||||
{ 1258ULL, -69 },
|
||||
{ 1584ULL, -68 },
|
||||
{ 1995ULL, -67 },
|
||||
{ 2511ULL, -66 },
|
||||
{ 3162ULL, -65 },
|
||||
{ 3981ULL, -64 },
|
||||
{ 5011ULL, -63 },
|
||||
{ 6309ULL, -62 },
|
||||
{ 7943ULL, -61 },
|
||||
{ 10000ULL, -60 },
|
||||
{ 12589ULL, -59 },
|
||||
{ 15848ULL, -58 },
|
||||
{ 19952ULL, -57 },
|
||||
{ 25118ULL, -56 },
|
||||
{ 31622ULL, -55 },
|
||||
{ 39810ULL, -54 },
|
||||
{ 50118ULL, -53 },
|
||||
{ 63095ULL, -52 },
|
||||
{ 79432ULL, -51 },
|
||||
{ 100000ULL, -50 },
|
||||
{ 125892ULL, -49 },
|
||||
{ 158489ULL, -48 },
|
||||
{ 199526ULL, -47 },
|
||||
{ 251188ULL, -46 },
|
||||
{ 316227ULL, -45 },
|
||||
{ 398107ULL, -44 },
|
||||
{ 501187ULL, -43 },
|
||||
{ 630957ULL, -42 },
|
||||
{ 794328ULL, -41 },
|
||||
{ 1000000ULL, -40 },
|
||||
{ 1258925ULL, -39 },
|
||||
{ 1584893ULL, -38 },
|
||||
{ 1995262ULL, -37 },
|
||||
{ 2511886ULL, -36 },
|
||||
{ 3162277ULL, -35 },
|
||||
{ 3981071ULL, -34 },
|
||||
{ 5011872ULL, -33 },
|
||||
{ 6309573ULL, -32 },
|
||||
{ 7943282ULL, -31 },
|
||||
{ 10000000ULL, -30 },
|
||||
{ 12589254ULL, -29 },
|
||||
{ 15848931ULL, -28 },
|
||||
{ 19952623ULL, -27 },
|
||||
{ 25118864ULL, -26 },
|
||||
{ 31622776ULL, -25 },
|
||||
{ 39810717ULL, -24 },
|
||||
{ 50118723ULL, -23 },
|
||||
{ 63095734ULL, -22 },
|
||||
{ 79432823ULL, -21 },
|
||||
{ 100000000ULL, -20 },
|
||||
{ 125892541ULL, -19 },
|
||||
{ 158489319ULL, -18 },
|
||||
{ 199526231ULL, -17 },
|
||||
{ 251188643ULL, -16 },
|
||||
{ 316227766ULL, -15 },
|
||||
{ 398107170ULL, -14 },
|
||||
{ 501187233ULL, -13 },
|
||||
{ 630957344ULL, -12 },
|
||||
{ 794328234ULL, -11 },
|
||||
{ 1000000000ULL, -10 },
|
||||
{ 1258925411ULL, -9 },
|
||||
{ 1584893192ULL, -8 },
|
||||
{ 1995262314ULL, -7 },
|
||||
{ 2511886431ULL, -6 },
|
||||
{ 3162277660ULL, -5 },
|
||||
{ 3981071705ULL, -4 },
|
||||
{ 5011872336ULL, -3 },
|
||||
{ 6309573444ULL, -2 },
|
||||
{ 7943282347ULL, -1 },
|
||||
{ 10000000000ULL, 0 },
|
||||
{ 12589254117ULL, 1 },
|
||||
{ 15848931924ULL, 2 },
|
||||
{ 19952623149ULL, 3 },
|
||||
{ 25118864315ULL, 4 },
|
||||
{ 31622776601ULL, 5 },
|
||||
{ 39810717055ULL, 6 },
|
||||
{ 50118723362ULL, 7 },
|
||||
{ 63095734448ULL, 8 },
|
||||
{ 79432823472ULL, 9 },
|
||||
{ 100000000000ULL, 10 },
|
||||
{ 125892541179ULL, 11 },
|
||||
{ 158489319246ULL, 12 },
|
||||
{ 199526231496ULL, 13 },
|
||||
{ 251188643150ULL, 14 },
|
||||
{ 316227766016ULL, 15 },
|
||||
{ 398107170553ULL, 16 },
|
||||
{ 501187233627ULL, 17 },
|
||||
{ 630957344480ULL, 18 },
|
||||
{ 794328234724ULL, 19 },
|
||||
{ 1000000000000ULL, 20 },
|
||||
{ 1258925411794ULL, 21 },
|
||||
{ 1584893192461ULL, 22 },
|
||||
{ 1995262314968ULL, 23 },
|
||||
{ 2511886431509ULL, 24 },
|
||||
};
|
||||
|
||||
int dect_mw_to_dbm(u64 mw)
|
||||
{
|
||||
unsigned int min, max, mid;
|
||||
u64 val;
|
||||
|
||||
min = 0;
|
||||
max = ARRAY_SIZE(mw_to_dbm_tbl) - 1;
|
||||
|
||||
while (min < max) {
|
||||
mid = min + (max - min) / 2;
|
||||
|
||||
val = mw_to_dbm_tbl[mid].mw;
|
||||
if (val < mw)
|
||||
min = mid + 1;
|
||||
else
|
||||
max = mid;
|
||||
}
|
||||
|
||||
if (val > mw) {
|
||||
if (mid == 0)
|
||||
return 0;
|
||||
mid--;
|
||||
}
|
||||
|
||||
return mw_to_dbm_tbl[mid].dbm;
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* DECT virtual transceiver
|
||||
*
|
||||
* Copyright (c) 2010 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <net/dect/transceiver.h>
|
||||
#include "vtrx.h"
|
||||
|
||||
static struct class *dect_class;
|
||||
|
||||
/*
|
||||
* Transceivers
|
||||
*/
|
||||
|
||||
#define VTRX_ATTR(_name, _mode, _show, _store) \
|
||||
struct device_attribute vtrx_attr_##_name = __ATTR(_name, _mode, _show, _store)
|
||||
|
||||
#define VTRX_NUMERIC_ATTR(name, field, scale) \
|
||||
static ssize_t vtrx_show_##name(struct device *dev, struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct dect_vtrx *vtrx = dev_get_drvdata(dev); \
|
||||
return sprintf(buf, "%llu\n", \
|
||||
(unsigned long long)div64_u64(vtrx->field, scale)); \
|
||||
} \
|
||||
\
|
||||
static ssize_t vtrx_store_##name(struct device *dev, struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
struct dect_vtrx *vtrx = dev_get_drvdata(dev); \
|
||||
char *ptr; \
|
||||
u32 val; \
|
||||
\
|
||||
val = simple_strtoul(buf, &ptr, 10); \
|
||||
if (ptr == buf) \
|
||||
return -EINVAL; \
|
||||
vtrx->field = val * scale; \
|
||||
return count; \
|
||||
} \
|
||||
static VTRX_ATTR(name, S_IRUGO | S_IWUSR, vtrx_show_##name, vtrx_store_##name)
|
||||
|
||||
VTRX_NUMERIC_ATTR(tx_power, tx_power, DECT_VTRX_POWER_SCALE);
|
||||
VTRX_NUMERIC_ATTR(pos_x, pos_x, 1000);
|
||||
VTRX_NUMERIC_ATTR(pos_y, pos_y, 1000);
|
||||
|
||||
static ssize_t vtrx_store_remove(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct dect_vtrx *vtrx = dev_get_drvdata(dev);
|
||||
|
||||
dect_vtrx_free(vtrx);
|
||||
return count;
|
||||
}
|
||||
|
||||
static VTRX_ATTR(remove, S_IWUSR, NULL, vtrx_store_remove);
|
||||
|
||||
static struct attribute *vtrx_attrs[] = {
|
||||
&vtrx_attr_tx_power.attr,
|
||||
&vtrx_attr_pos_x.attr,
|
||||
&vtrx_attr_pos_y.attr,
|
||||
&vtrx_attr_remove.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group vtrx_attr_group = {
|
||||
.attrs = vtrx_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *vtrx_attr_groups[] = {
|
||||
&vtrx_attr_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void dect_vtrx_release(struct device *dev)
|
||||
{
|
||||
printk("%s\n", __func__);
|
||||
}
|
||||
|
||||
static struct device_type dect_vtrx_group = {
|
||||
.name = "vtrx",
|
||||
.groups = vtrx_attr_groups,
|
||||
.release = dect_vtrx_release,
|
||||
};
|
||||
|
||||
int dect_vtrx_register_sysfs(struct dect_vtrx *vtrx)
|
||||
{
|
||||
struct device *dev = &vtrx->dev;
|
||||
|
||||
dev->type = &dect_vtrx_group;
|
||||
dev->class = dect_class;
|
||||
dev->parent = &vtrx->group->dev;
|
||||
|
||||
dev_set_name(dev, "%s", vtrx->trx->name);
|
||||
dev_set_drvdata(dev, vtrx);
|
||||
|
||||
return device_register(dev);
|
||||
}
|
||||
|
||||
void dect_vtrx_unregister_sysfs(struct dect_vtrx *vtrx)
|
||||
{
|
||||
device_del(&vtrx->dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Groups
|
||||
*/
|
||||
|
||||
#define GROUP_ATTR(_name, _mode, _show, _store) \
|
||||
struct device_attribute group_attr_##_name = __ATTR(_name, _mode, _show, _store)
|
||||
|
||||
#define GROUP_SHOW(name, field, fmt) \
|
||||
static ssize_t group_show_##name(struct device *dev, struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct dect_vtrx_group *group = dev_get_drvdata(dev); \
|
||||
return sprintf(buf, fmt, group->field); \
|
||||
} \
|
||||
\
|
||||
static ssize_t group_store_##name(struct device *dev, struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
struct dect_vtrx_group *group = dev_get_drvdata(dev); \
|
||||
char *ptr; \
|
||||
u32 val; \
|
||||
\
|
||||
val = simple_strtoul(buf, &ptr, 10); \
|
||||
if (ptr == buf) \
|
||||
return -EINVAL; \
|
||||
group->field = val; \
|
||||
return count; \
|
||||
} \
|
||||
static GROUP_ATTR(name, S_IRUGO | S_IWUSR, group_show_##name, group_store_##name)
|
||||
|
||||
static ssize_t group_store_new(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct dect_vtrx_group *group = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
err = dect_vtrx_init(group);
|
||||
return err ? err : count;
|
||||
}
|
||||
|
||||
static GROUP_ATTR(new_trx, S_IWUSR, NULL, group_store_new);
|
||||
|
||||
static struct attribute *group_attrs[] = {
|
||||
&group_attr_new_trx.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group group_attr_group = {
|
||||
.attrs = group_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *group_attr_groups[] = {
|
||||
&group_attr_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void dect_vtrx_group_release(struct device *dev)
|
||||
{
|
||||
printk("%s\n", __func__);
|
||||
}
|
||||
|
||||
static struct device_type dect_vtrx_group_group = {
|
||||
.name = "vtrx-group",
|
||||
.groups = group_attr_groups,
|
||||
.release = dect_vtrx_group_release,
|
||||
};
|
||||
|
||||
int dect_vtrx_group_register_sysfs(struct dect_vtrx_group *group)
|
||||
{
|
||||
struct device *dev = &group->dev;
|
||||
|
||||
dev->type = &dect_vtrx_group_group;
|
||||
dev->class = dect_class;
|
||||
dev->parent = 0;
|
||||
|
||||
dev_set_name(dev, "%s", group->name);
|
||||
dev_set_drvdata(dev, group);
|
||||
|
||||
return device_register(dev);
|
||||
}
|
||||
|
||||
static ssize_t store_new_group(struct class *dev, struct class_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
char name[16];
|
||||
|
||||
sscanf(buf, "%16s", name);
|
||||
if (!dect_vtrx_group_init(name))
|
||||
return -ENOMEM;
|
||||
return count;
|
||||
}
|
||||
|
||||
static CLASS_ATTR(new_group, S_IWUSR, NULL, store_new_group);
|
||||
|
||||
void dect_vtrx_group_unregister_sysfs(struct dect_vtrx_group *group)
|
||||
{
|
||||
device_del(&group->dev);
|
||||
}
|
||||
|
||||
int dect_vtrx_sysfs_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
dect_class = class_create(THIS_MODULE, "dect");
|
||||
if (dect_class == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
err = class_create_file(dect_class, &class_attr_new_group);
|
||||
if (err < 0)
|
||||
class_destroy(dect_class);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void dect_vtrx_sysfs_exit(void)
|
||||
{
|
||||
class_remove_file(dect_class, &class_attr_new_group);
|
||||
class_destroy(dect_class);
|
||||
}
|
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
* DECT virtual transceiver
|
||||
*
|
||||
* Copyright (c) 2010 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define DEBUG
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/dect/transceiver.h>
|
||||
#include "vtrx.h"
|
||||
|
||||
#define vtrx_debug(vtrx, fmt, args...) \
|
||||
pr_debug("vtrx %s: " fmt, (vtrx)->trx->name, ## args)
|
||||
|
||||
#define DECT_SLOTS_PER_SECOND (DECT_FRAMES_PER_SECOND * DECT_FRAME_SIZE)
|
||||
#define DECT_VTRX_RATE (NSEC_PER_SEC / DECT_SLOTS_PER_SECOND)
|
||||
#define DECT_VTRX_DEFAULT_TRX 2
|
||||
|
||||
#define DECT_WAVELEN_SCALE 13
|
||||
#define DECT_WAVELEN 160 /* mm */
|
||||
|
||||
struct dect_skb_vtrx_cb {
|
||||
struct dect_vtrx *vtrx;
|
||||
u8 rssi;
|
||||
u8 carrier;
|
||||
};
|
||||
|
||||
static LIST_HEAD(vtrx_groups);
|
||||
|
||||
static inline struct dect_skb_vtrx_cb *DECT_VTRX_CB(const struct sk_buff *skb)
|
||||
{
|
||||
BUILD_BUG_ON(sizeof(struct dect_skb_vtrx_cb) > sizeof(skb->cb));
|
||||
return (struct dect_skb_vtrx_cb *)skb->cb;
|
||||
}
|
||||
|
||||
static unsigned int dect_vtrx_distance(const struct dect_vtrx *vtrx1,
|
||||
const struct dect_vtrx *vtrx2)
|
||||
{
|
||||
int dx, dy;
|
||||
|
||||
dx = vtrx1->pos_x - vtrx2->pos_x;
|
||||
dy = vtrx1->pos_y - vtrx2->pos_y;
|
||||
|
||||
return int_sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
static u8 dect_vtrx_receive_rssi(const struct dect_vtrx *rx_vtrx,
|
||||
const struct sk_buff *skb)
|
||||
{
|
||||
const struct dect_vtrx *tx_vtrx = DECT_VTRX_CB(skb)->vtrx;
|
||||
unsigned int distance;
|
||||
u64 rx_power, tmp;
|
||||
int dbm = 0;
|
||||
|
||||
distance = dect_vtrx_distance(rx_vtrx, tx_vtrx);
|
||||
if (distance == 0)
|
||||
goto out;
|
||||
|
||||
tmp = 1000 * (DECT_WAVELEN << DECT_WAVELEN_SCALE) / (4 * 3141 * distance);
|
||||
rx_power = (tx_vtrx->tx_power * tmp * tmp) >> (2 * DECT_WAVELEN_SCALE);
|
||||
dbm = dect_mw_to_dbm(rx_power);
|
||||
out:
|
||||
if (dbm > -33)
|
||||
dbm = -33;
|
||||
|
||||
return dect_dbm_to_rssi(dbm);
|
||||
}
|
||||
|
||||
static void dect_vtrx_process_slot(struct dect_vtrx_group *group,
|
||||
struct dect_vtrx *vtrx)
|
||||
{
|
||||
struct dect_transceiver_event *event;
|
||||
struct dect_transceiver_slot *ts;
|
||||
struct dect_transceiver *trx = vtrx->trx;
|
||||
struct sk_buff *skb, *best;
|
||||
u8 slot = group->slot, rcvslot;
|
||||
u8 rssi, best_rssi;
|
||||
|
||||
event = dect_transceiver_event(trx, slot % 12, slot);
|
||||
if (event == NULL)
|
||||
return;
|
||||
|
||||
if (trx->state == DECT_TRANSCEIVER_UNLOCKED ||
|
||||
trx->state == DECT_TRANSCEIVER_LOCK_PENDING)
|
||||
rcvslot = DECT_SCAN_SLOT;
|
||||
else
|
||||
rcvslot = slot;
|
||||
|
||||
rssi = dect_dbm_to_rssi(-80);
|
||||
best = NULL;
|
||||
|
||||
ts = &trx->slots[rcvslot];
|
||||
if (ts->state != DECT_SLOT_RX &&
|
||||
ts->state != DECT_SLOT_SCANNING)
|
||||
goto queue;
|
||||
|
||||
skb_queue_walk(&group->txq[slot], skb) {
|
||||
if (DECT_VTRX_CB(skb)->carrier != ts->chd.carrier)
|
||||
continue;
|
||||
|
||||
rssi = dect_vtrx_receive_rssi(vtrx, skb);
|
||||
if (best == NULL || rssi > best_rssi) {
|
||||
best = skb;
|
||||
best_rssi = rssi;
|
||||
}
|
||||
}
|
||||
|
||||
if (best == NULL)
|
||||
goto rssi;
|
||||
rssi = best_rssi;
|
||||
|
||||
skb = skb_clone(best, GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
goto rssi;
|
||||
|
||||
DECT_TRX_CB(skb)->trx = trx;
|
||||
DECT_TRX_CB(skb)->slot = rcvslot;
|
||||
DECT_TRX_CB(skb)->csum = DECT_CHECKSUM_A_CRC_OK | DECT_CHECKSUM_X_CRC_OK;
|
||||
DECT_TRX_CB(skb)->rssi = rssi;
|
||||
__skb_queue_tail(&event->rx_queue, skb);
|
||||
|
||||
ts->rx_bytes += skb->len;
|
||||
ts->rx_packets++;
|
||||
rssi:
|
||||
ts->rssi = dect_average_rssi(ts->rssi, rssi);
|
||||
dect_transceiver_record_rssi(event, rcvslot, rssi);
|
||||
queue:
|
||||
if (rcvslot != slot && best == NULL)
|
||||
dect_release_transceiver_event(event);
|
||||
else
|
||||
dect_transceiver_queue_event(trx, event);
|
||||
}
|
||||
|
||||
static enum hrtimer_restart dect_vtrx_timer(struct hrtimer *timer)
|
||||
{
|
||||
struct dect_vtrx_group *group = container_of(timer, struct dect_vtrx_group, timer);
|
||||
struct dect_vtrx *vtrx;
|
||||
ktime_t time;
|
||||
|
||||
list_for_each_entry(vtrx, &group->act_list, list)
|
||||
dect_vtrx_process_slot(group, vtrx);
|
||||
|
||||
skb_queue_purge(&group->txq[group->slot]);
|
||||
group->slot = dect_next_slotnum(group->slot);
|
||||
|
||||
time = ktime_set(0, DECT_VTRX_RATE);
|
||||
hrtimer_forward(timer, hrtimer_cb_get_time(timer), time);
|
||||
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transceiver operations
|
||||
*/
|
||||
|
||||
static void dect_vtrx_enable(const struct dect_transceiver *trx)
|
||||
{
|
||||
struct dect_vtrx *vtrx = dect_transceiver_priv(trx);
|
||||
struct dect_vtrx_group *group = vtrx->group;
|
||||
ktime_t time;
|
||||
|
||||
vtrx_debug(vtrx, "enable");
|
||||
if (list_empty(&group->act_list)) {
|
||||
time = ktime_set(0, DECT_VTRX_RATE);
|
||||
hrtimer_start(&group->timer, time, HRTIMER_MODE_ABS);
|
||||
}
|
||||
list_move_tail(&vtrx->list, &group->act_list);
|
||||
}
|
||||
|
||||
static void dect_vtrx_disable(const struct dect_transceiver *trx)
|
||||
{
|
||||
struct dect_vtrx *vtrx = dect_transceiver_priv(trx);
|
||||
struct dect_vtrx_group *group = vtrx->group;
|
||||
|
||||
vtrx_debug(vtrx, "disable");
|
||||
list_move_tail(&vtrx->list, &group->trx_list);
|
||||
if (list_empty(&group->act_list))
|
||||
hrtimer_cancel(&group->timer);
|
||||
}
|
||||
|
||||
static void dect_vtrx_confirm(const struct dect_transceiver *trx)
|
||||
{
|
||||
struct dect_vtrx *vtrx = dect_transceiver_priv(trx);
|
||||
|
||||
vtrx_debug(vtrx, "confirm");
|
||||
}
|
||||
|
||||
static void dect_vtrx_unlock(const struct dect_transceiver *trx)
|
||||
{
|
||||
struct dect_vtrx *vtrx = dect_transceiver_priv(trx);
|
||||
|
||||
vtrx_debug(vtrx, "unlock");
|
||||
}
|
||||
|
||||
static void dect_vtrx_lock(const struct dect_transceiver *trx, u8 slot)
|
||||
{
|
||||
struct dect_vtrx *vtrx = dect_transceiver_priv(trx);
|
||||
|
||||
vtrx_debug(vtrx, "lock");
|
||||
}
|
||||
|
||||
static void dect_vtrx_set_mode(const struct dect_transceiver *trx,
|
||||
const struct dect_channel_desc *chd,
|
||||
enum dect_slot_states mode)
|
||||
{
|
||||
struct dect_vtrx *vtrx = dect_transceiver_priv(trx);
|
||||
|
||||
vtrx_debug(vtrx, "set_mode: slot: %u mode: %u",
|
||||
chd->slot, mode);
|
||||
}
|
||||
|
||||
static void dect_vtrx_set_carrier(const struct dect_transceiver *trx,
|
||||
u8 slot, u8 carrier)
|
||||
{
|
||||
struct dect_vtrx *vtrx = dect_transceiver_priv(trx);
|
||||
|
||||
vtrx_debug(vtrx, "set carrier: slot: %u carrier: %u\n",
|
||||
slot, carrier);
|
||||
}
|
||||
|
||||
static u64 dect_vtrx_set_band(const struct dect_transceiver *trx,
|
||||
const struct dect_band *band)
|
||||
{
|
||||
struct dect_vtrx *vtrx = dect_transceiver_priv(trx);
|
||||
|
||||
vtrx_debug(vtrx, "set band: %u\n", band->band);
|
||||
return band->carriers;
|
||||
}
|
||||
|
||||
static void dect_vtrx_tx(const struct dect_transceiver *trx, struct sk_buff *skb)
|
||||
{
|
||||
struct dect_vtrx *vtrx = dect_transceiver_priv(trx);
|
||||
struct dect_vtrx_group *group = vtrx->group;
|
||||
u8 slot = DECT_TRX_CB(skb)->slot;
|
||||
|
||||
vtrx_debug(vtrx, "vtrx tx: slot: %u skb: %p\n", slot, skb);
|
||||
DECT_VTRX_CB(skb)->vtrx = vtrx;
|
||||
DECT_VTRX_CB(skb)->rssi = vtrx->tx_power;
|
||||
DECT_VTRX_CB(skb)->carrier = trx->slots[slot].chd.carrier;
|
||||
skb_queue_tail(&group->txq[slot], skb);
|
||||
}
|
||||
|
||||
static const struct dect_transceiver_ops vtrx_transceiver_ops = {
|
||||
.name = "vtrx",
|
||||
.slotmask = 0xffffff,
|
||||
.eventrate = 1,
|
||||
.latency = 1,
|
||||
.enable = dect_vtrx_enable,
|
||||
.disable = dect_vtrx_disable,
|
||||
.confirm = dect_vtrx_confirm,
|
||||
.unlock = dect_vtrx_unlock,
|
||||
.lock = dect_vtrx_lock,
|
||||
.set_mode = dect_vtrx_set_mode,
|
||||
.set_carrier = dect_vtrx_set_carrier,
|
||||
.set_band = dect_vtrx_set_band,
|
||||
.tx = dect_vtrx_tx,
|
||||
.destructor = dect_transceiver_free,
|
||||
};
|
||||
|
||||
int dect_vtrx_init(struct dect_vtrx_group *group)
|
||||
{
|
||||
struct dect_transceiver *trx;
|
||||
struct dect_vtrx *vtrx;
|
||||
int err;
|
||||
|
||||
trx = dect_transceiver_alloc(&vtrx_transceiver_ops, sizeof(*vtrx));
|
||||
if (trx == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
err = dect_register_transceiver(trx);
|
||||
if (err < 0)
|
||||
goto err1;
|
||||
|
||||
vtrx = dect_transceiver_priv(trx);
|
||||
vtrx->group = group;
|
||||
vtrx->trx = trx;
|
||||
vtrx->tx_power = 2 * DECT_VTRX_POWER_SCALE;
|
||||
list_add_tail(&vtrx->list, &group->trx_list);
|
||||
|
||||
dect_vtrx_register_sysfs(vtrx);
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
dect_transceiver_free(trx);
|
||||
return err;
|
||||
}
|
||||
|
||||
void dect_vtrx_free(struct dect_vtrx *vtrx)
|
||||
{
|
||||
dect_vtrx_unregister_sysfs(vtrx);
|
||||
dect_unregister_transceiver(vtrx->trx);
|
||||
}
|
||||
|
||||
struct dect_vtrx_group *dect_vtrx_group_init(const char *name)
|
||||
{
|
||||
struct dect_vtrx_group *group;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
group = kzalloc(sizeof(*group), GFP_KERNEL);
|
||||
if (group == NULL)
|
||||
goto err1;
|
||||
|
||||
strlcpy(group->name, name, sizeof(group->name));
|
||||
INIT_LIST_HEAD(&group->trx_list);
|
||||
INIT_LIST_HEAD(&group->act_list);
|
||||
hrtimer_init(&group->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||
group->timer.function = dect_vtrx_timer;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(group->txq); i++)
|
||||
skb_queue_head_init(&group->txq[i]);
|
||||
|
||||
err = dect_vtrx_group_register_sysfs(group);
|
||||
if (err < 0)
|
||||
goto err2;
|
||||
|
||||
list_add_tail(&group->list, &vtrx_groups);
|
||||
return group;
|
||||
|
||||
err2:
|
||||
kfree(group);
|
||||
err1:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dect_vtrx_group_free(struct dect_vtrx_group *group)
|
||||
{
|
||||
struct dect_vtrx *vtrx, *next;
|
||||
unsigned int i;
|
||||
|
||||
list_for_each_entry_safe(vtrx, next, &group->act_list, list)
|
||||
dect_vtrx_free(vtrx);
|
||||
list_for_each_entry_safe(vtrx, next, &group->trx_list, list)
|
||||
dect_vtrx_free(vtrx);
|
||||
|
||||
dect_vtrx_group_unregister_sysfs(group);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(group->txq); i++)
|
||||
__skb_queue_purge(&group->txq[i]);
|
||||
|
||||
kfree(group);
|
||||
}
|
||||
|
||||
static int __init vtrx_init(void)
|
||||
{
|
||||
struct dect_vtrx_group *group;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
err = dect_vtrx_sysfs_init();
|
||||
if (err < 0)
|
||||
goto err1;
|
||||
|
||||
group = dect_vtrx_group_init("group-1");
|
||||
if (group == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
for (i = 0; i < DECT_VTRX_DEFAULT_TRX; i++) {
|
||||
err = dect_vtrx_init(group);
|
||||
if (err < 0)
|
||||
goto err3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
dect_vtrx_group_free(group);
|
||||
err2:
|
||||
dect_vtrx_sysfs_exit();
|
||||
err1:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit vtrx_exit(void)
|
||||
{
|
||||
struct dect_vtrx_group *group, *next;
|
||||
|
||||
list_for_each_entry_safe(group, next, &vtrx_groups, list)
|
||||
dect_vtrx_group_free(group);
|
||||
|
||||
dect_vtrx_sysfs_exit();
|
||||
}
|
||||
|
||||
module_init(vtrx_init);
|
||||
module_exit(vtrx_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef _DECT_VTRX_H
|
||||
#define _DECT_VTRX_H
|
||||
|
||||
struct dect_vtrx_group {
|
||||
struct list_head list;
|
||||
struct device dev;
|
||||
char name[16];
|
||||
struct hrtimer timer;
|
||||
struct list_head trx_list;
|
||||
struct list_head act_list;
|
||||
struct sk_buff_head txq[DECT_FRAME_SIZE];
|
||||
unsigned int slot;
|
||||
};
|
||||
|
||||
struct dect_vtrx {
|
||||
struct list_head list;
|
||||
struct device dev;
|
||||
struct dect_vtrx_group *group;
|
||||
struct dect_transceiver *trx;
|
||||
u64 tx_power;
|
||||
unsigned int pos_x;
|
||||
unsigned int pos_y;
|
||||
};
|
||||
|
||||
extern struct dect_vtrx_group *dect_vtrx_group_init(const char *name);
|
||||
extern void dect_vtrx_group_free(struct dect_vtrx_group *group);
|
||||
extern int dect_vtrx_group_register_sysfs(struct dect_vtrx_group *group);
|
||||
extern void dect_vtrx_group_unregister_sysfs(struct dect_vtrx_group *group);
|
||||
|
||||
extern int dect_vtrx_register_sysfs(struct dect_vtrx *vtrx);
|
||||
extern void dect_vtrx_unregister_sysfs(struct dect_vtrx *vtrx);
|
||||
extern int dect_vtrx_init(struct dect_vtrx_group *group);
|
||||
extern void dect_vtrx_free(struct dect_vtrx *vtrx);
|
||||
|
||||
extern int dect_vtrx_sysfs_init(void);
|
||||
extern void dect_vtrx_sysfs_exit(void);
|
||||
|
||||
#define DECT_VTRX_POWER_SCALE 10000000000ULL
|
||||
|
||||
extern int dect_mw_to_dbm(u64 mw);
|
||||
|
||||
#endif /* _DECT_VTRX_H */
|
Reference in New Issue