diff --git a/drivers/dect/Kconfig b/drivers/dect/Kconfig index 1611d45b49c..baba4c10412 100644 --- a/drivers/dect/Kconfig +++ b/drivers/dect/Kconfig @@ -5,6 +5,7 @@ menuconfig DECTDEVICES if DECTDEVICES +source "drivers/dect/vtrx/Kconfig" source "drivers/dect/coa/Kconfig" endif diff --git a/drivers/dect/Makefile b/drivers/dect/Makefile index f4a9ebe2bcf..c58af58e2b8 100644 --- a/drivers/dect/Makefile +++ b/drivers/dect/Makefile @@ -1 +1,2 @@ +obj-$(CONFIG_DECT_VTRX) += vtrx/ obj-$(CONFIG_DECT_COA) += coa/ diff --git a/drivers/dect/vtrx/Kconfig b/drivers/dect/vtrx/Kconfig new file mode 100644 index 00000000000..fb86eeefa8a --- /dev/null +++ b/drivers/dect/vtrx/Kconfig @@ -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. diff --git a/drivers/dect/vtrx/Makefile b/drivers/dect/vtrx/Makefile new file mode 100644 index 00000000000..08abbf52c91 --- /dev/null +++ b/drivers/dect/vtrx/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_DECT_VTRX) += dect-vtrx.o +dect-vtrx-objs := vtrx.o vtrx-sysfs.o mw_to_dbm.o diff --git a/drivers/dect/vtrx/mw_to_dbm.c b/drivers/dect/vtrx/mw_to_dbm.c new file mode 100644 index 00000000000..55122a88cd2 --- /dev/null +++ b/drivers/dect/vtrx/mw_to_dbm.c @@ -0,0 +1,164 @@ +/* + * DECT virtual transceiver + * + * Copyright (c) 2010 Patrick McHardy + * + * 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 +#include +#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; +} diff --git a/drivers/dect/vtrx/vtrx-sysfs.c b/drivers/dect/vtrx/vtrx-sysfs.c new file mode 100644 index 00000000000..d65beabc05b --- /dev/null +++ b/drivers/dect/vtrx/vtrx-sysfs.c @@ -0,0 +1,228 @@ +/* + * DECT virtual transceiver + * + * Copyright (c) 2010 Patrick McHardy + * + * 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 +#include +#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); +} diff --git a/drivers/dect/vtrx/vtrx.c b/drivers/dect/vtrx/vtrx.c new file mode 100644 index 00000000000..69c1b94b798 --- /dev/null +++ b/drivers/dect/vtrx/vtrx.c @@ -0,0 +1,398 @@ +/* + * DECT virtual transceiver + * + * Copyright (c) 2010 Patrick McHardy + * + * 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 +#include +#include +#include +#include +#include +#include +#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"); diff --git a/drivers/dect/vtrx/vtrx.h b/drivers/dect/vtrx/vtrx.h new file mode 100644 index 00000000000..67aaba67bc2 --- /dev/null +++ b/drivers/dect/vtrx/vtrx.h @@ -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 */