dect
/
linux-2.6
Archived
13
0
Fork 0

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:
Patrick McHardy 2010-03-02 08:43:49 +01:00
parent c06cee2d91
commit e4ed1a3529
8 changed files with 841 additions and 0 deletions

View File

@ -5,6 +5,7 @@ menuconfig DECTDEVICES
if DECTDEVICES
source "drivers/dect/vtrx/Kconfig"
source "drivers/dect/coa/Kconfig"
endif

View File

@ -1 +1,2 @@
obj-$(CONFIG_DECT_VTRX) += vtrx/
obj-$(CONFIG_DECT_COA) += coa/

View File

@ -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.

View File

@ -0,0 +1,2 @@
obj-$(CONFIG_DECT_VTRX) += dect-vtrx.o
dect-vtrx-objs := vtrx.o vtrx-sysfs.o mw_to_dbm.o

View File

@ -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;
}

View File

@ -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);
}

398
drivers/dect/vtrx/vtrx.c Normal file
View File

@ -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");

42
drivers/dect/vtrx/vtrx.h Normal file
View File

@ -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 */