[OpenWrt-Devel] [Patch][(kernel|generic)] Add port ingress rate limit function for AR8337N switch
luny at codeaurora.org
luny at codeaurora.org
Wed Jul 2 03:22:27 EDT 2014
hi, all:
now we want to add AR8337 switch feature into openwrt, can you give me
some suggestion about how to add these features. So I give one
example, hope your feedback.
> hi, all:
> we will try to add one feature for AR8337 switch: port ingress rate limit.
> we can use swconfig extension command to implement it as below example:
> swconfig dev eth0 set extension_switch "port_rate_limit set port 1 rate
> 100000"
> Note: in this example, 100000 means 100000Kbps.
> 1. We use one special communication command in swconfig "extension_switch"
> to transfer the extension command for AR8337 switch enhanced feature.
>
>
> Signed-off-by: Yue Lun <luny at codeaurora.org>
>
> ---
> .../linux/generic/files/drivers/net/phy/ar8216.c | 10 +
> .../generic/files/drivers/net/phy/ar8216_ext.c | 792
> +++++++++++++++++++++
> .../generic/files/drivers/net/phy/ar8216_ext.h | 269 +++++++
> .../generic/patches-3.10/724-phy_ar8216.patch | 2 +-
> .../generic/patches-3.10/725-phy_rtl8306.patch | 2 +-
> .../generic/patches-3.10/726-phy_rtl8366.patch | 2 +-
> .../generic/patches-3.14/724-phy_ar8216.patch | 2 +-
> 7 files changed, 1075 insertions(+), 4 deletions(-)
> mode change 100644 => 100755
> target/linux/generic/files/drivers/net/phy/ar8216.c
> create mode 100755
> target/linux/generic/files/drivers/net/phy/ar8216_ext.c
> create mode 100755
> target/linux/generic/files/drivers/net/phy/ar8216_ext.h
> mode change 100644 => 100755
> target/linux/generic/patches-3.10/724-phy_ar8216.patch
> mode change 100644 => 100755
> target/linux/generic/patches-3.10/725-phy_rtl8306.patch
> mode change 100644 => 100755
> target/linux/generic/patches-3.10/726-phy_rtl8366.patch
> mode change 100644 => 100755
> target/linux/generic/patches-3.14/724-phy_ar8216.patch
>
> diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c
> b/target/linux/generic/files/drivers/net/phy/ar8216.c
> old mode 100644
> new mode 100755
> index 3f60878..bf5a078
> --- a/target/linux/generic/files/drivers/net/phy/ar8216.c
> +++ b/target/linux/generic/files/drivers/net/phy/ar8216.c
> @@ -38,6 +38,7 @@
> #include <linux/gpio.h>
>
> #include "ar8216.h"
> +#include "ar8216_ext.h"
>
> /* size of the vlan table */
> #define AR8X16_MAX_VLANS 128
> @@ -2389,6 +2390,12 @@ static struct switch_attr ar8327_sw_attr_globals[]
> = {
> .get = ar8xxx_sw_get_mirror_source_port,
> .max = AR8327_NUM_PORTS - 1
> },
> + {
> + .type = SWITCH_TYPE_STRING,
> + .name = "extension_switch",
> + .description = "Enhanced switch feature",
> + .set = ar8xxx_sw_set_extension_switch,
> + },
> };
>
> static struct switch_attr ar8xxx_sw_attr_port[] = {
> @@ -2669,6 +2676,7 @@ ar8xxx_probe_switch(struct ar8xxx_priv *priv)
> return 0;
> }
>
> +extern int ar8216_ext_init(struct ar8xxx_priv *priv);
> static int
> ar8xxx_start(struct ar8xxx_priv *priv)
> {
> @@ -2688,6 +2696,8 @@ ar8xxx_start(struct ar8xxx_priv *priv)
>
> ar8xxx_mib_start(priv);
>
> + ar8216_ext_init(priv);
> +
> return 0;
> }
>
> diff --git a/target/linux/generic/files/drivers/net/phy/ar8216_ext.c
> b/target/linux/generic/files/drivers/net/phy/ar8216_ext.c
> new file mode 100755
> index 0000000..e31868f
> --- /dev/null
> +++ b/target/linux/generic/files/drivers/net/phy/ar8216_ext.c
> @@ -0,0 +1,792 @@
> +/*
> + * ar8216_ext.c: AR8216 switch driver enhanced feature
> + *
> + * Copyright (C) 2014 Zou Shunxiang <shunxian at qti.qualcomm.com>
> + *
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/if.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/list.h>
> +#include <linux/if_ether.h>
> +#include <linux/skbuff.h>
> +#include <linux/netdevice.h>
> +#include <linux/netlink.h>
> +#include <linux/bitops.h>
> +#include <net/genetlink.h>
> +#include <linux/switch.h>
> +#include <linux/delay.h>
> +#include <linux/phy.h>
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/lockdep.h>
> +#include <linux/ar8216_platform.h>
> +#include <linux/workqueue.h>
> +#include <linux/of_device.h>
> +#include <linux/leds.h>
> +#include <linux/gpio.h>
> +
> +#include "ar8216_ext.h"
> +#include "ar8216.h"
> +
> +/* size of the vlan table */
> +#define AR8X16_MAX_VLANS 128
> +#define AR8X16_PROBE_RETRIES 10
> +#define AR8X16_MAX_PORTS 8
> +struct ar8xxx_mib_desc {
> + unsigned int size;
> + unsigned int offset;
> + const char *name;
> +};
> +
> +struct ar8xxx_chip {
> + unsigned long caps;
> +
> + int (*hw_init)(struct ar8xxx_priv *priv);
> + void (*cleanup)(struct ar8xxx_priv *priv);
> +
> + void (*init_globals)(struct ar8xxx_priv *priv);
> + void (*init_port)(struct ar8xxx_priv *priv, int port);
> + void (*setup_port)(struct ar8xxx_priv *priv, int port, u32 egress,
> + u32 ingress, u32 members, u32 pvid);
> + u32 (*read_port_status)(struct ar8xxx_priv *priv, int port);
> + int (*atu_flush)(struct ar8xxx_priv *priv);
> + void (*vtu_flush)(struct ar8xxx_priv *priv);
> + void (*vtu_load_vlan)(struct ar8xxx_priv *priv, u32 vid, u32 port_mask);
> +
> + const struct ar8xxx_mib_desc *mib_decs;
> + unsigned num_mibs;
> +};
> +
> +enum ar8327_led_pattern {
> + AR8327_LED_PATTERN_OFF = 0,
> + AR8327_LED_PATTERN_BLINK,
> + AR8327_LED_PATTERN_ON,
> + AR8327_LED_PATTERN_RULE,
> +};
> +
> +struct ar8327_led {
> + struct led_classdev cdev;
> + struct ar8xxx_priv *sw_priv;
> +
> + char *name;
> + bool active_low;
> + u8 led_num;
> + enum ar8327_led_mode mode;
> +
> + struct mutex mutex;
> + spinlock_t lock;
> + struct work_struct led_work;
> + bool enable_hw_mode;
> + enum ar8327_led_pattern pattern;
> +};
> +
> +struct ar8327_data {
> + u32 port0_status;
> + u32 port6_status;
> +
> + struct ar8327_led **leds;
> + unsigned int num_leds;
> +};
> +
> +struct ar8xxx_priv {
> + struct switch_dev dev;
> + struct mii_bus *mii_bus;
> + struct phy_device *phy;
> +
> + u32 (*read)(struct ar8xxx_priv *priv, int reg);
> + void (*write)(struct ar8xxx_priv *priv, int reg, u32 val);
> + u32 (*rmw)(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val);
> +
> + int (*get_port_link)(unsigned port);
> +
> + const struct net_device_ops *ndo_old;
> + struct net_device_ops ndo;
> + struct mutex reg_mutex;
> + u8 chip_ver;
> + u8 chip_rev;
> + const struct ar8xxx_chip *chip;
> + union {
> + struct ar8327_data ar8327;
> + } chip_data;
> + bool initialized;
> + bool port4_phy;
> + char buf[2048];
> +
> + bool init;
> + bool mii_lo_first;
> +
> + struct mutex mib_lock;
> + struct delayed_work mib_work;
> + int mib_next_port;
> + u64 *mib_stats;
> +
> + struct list_head list;
> + unsigned int use_count;
> +
> + /* all fields below are cleared on reset */
> + bool vlan;
> + u16 vlan_id[AR8X16_MAX_VLANS];
> + u8 vlan_table[AR8X16_MAX_VLANS];
> + u8 vlan_tagged;
> + u16 pvid[AR8X16_MAX_PORTS];
> +
> + /* mirroring */
> + bool mirror_rx;
> + bool mirror_tx;
> + int source_port;
> + int monitor_port;
> +};
> +
> +static u8 ar8xxx_chip_version = 0xff;
> +struct ar8xxx_priv *ext_priv;
> +static hsl_api_t hsl_api_table;
> +
> +/* register functions */
> +#define SW_BIT_MASK_U32(nr) (~(0xFFFFFFFF << (nr)))
> +
> +#define SW_FIELD_MASK_U32(offset, len) \
> + ((SW_BIT_MASK_U32(len) << (offset)))
> +
> +#define SW_FIELD_MASK_NOT_U32(offset,len) \
> + (~(SW_BIT_MASK_U32(len) << (offset)))
> +
> +#define SW_FIELD_2_REG(field_val, bit_offset) \
> + (field_val << (bit_offset) )
> +
> +#define SW_REG_2_FIELD(reg_val, bit_offset, field_len) \
> + (((reg_val) >> (bit_offset)) & ((1 << (field_len)) - 1))
> +
> +#define SW_REG_SET_BY_FIELD_U32(reg_value, field_value, bit_offset,
> field_len)\
> + do { \
> + (reg_value) = \
> + (((reg_value) & SW_FIELD_MASK_NOT_U32((bit_offset),(field_len))) \
> + | (((field_value) & SW_BIT_MASK_U32(field_len)) << (bit_offset)));\
> + } while (0)
> +
> +#define SW_FIELD_GET_BY_REG_U32(reg_value, field_value, bit_offset,
> field_len)\
> + do { \
> + (field_value) = \
> + (((reg_value) >> (bit_offset)) & SW_BIT_MASK_U32(field_len)); \
> + } while (0)
> +
> +#define SW_SET_REG_BY_FIELD(reg, field, field_value, reg_value) \
> + SW_REG_SET_BY_FIELD_U32(reg_value, field_value, reg##_##field##_BOFFSET,
> \
> + reg##_##field##_BLEN)
> +
> +#define SW_GET_FIELD_BY_REG(reg, field, field_value, reg_value) \
> + SW_FIELD_GET_BY_REG_U32(reg_value, field_value, reg##_##field##_BOFFSET,
> \
> + reg##_##field##_BLEN)
> +
> +static inline void
> +split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
> +{
> + regaddr >>= 1;
> + *r1 = regaddr & 0x1e;
> +
> + regaddr >>= 5;
> + *r2 = regaddr & 0x7;
> +
> + regaddr >>= 3;
> + *page = regaddr & 0x1ff;
> +}
> +
> +static u32
> +ar8xxx_mii_read(struct ar8xxx_priv *priv, int reg)
> +{
> + struct mii_bus *bus = priv->mii_bus;
> + u16 r1, r2, page;
> + u16 lo, hi;
> +
> + split_addr((u32) reg, &r1, &r2, &page);
> +
> + mutex_lock(&bus->mdio_lock);
> +
> + bus->write(bus, 0x18, 0, page);
> + usleep_range(1000, 2000); /* wait for the page switch to propagate */
> + lo = bus->read(bus, 0x10 | r2, r1);
> + hi = bus->read(bus, 0x10 | r2, r1 + 1);
> +
> + mutex_unlock(&bus->mdio_lock);
> +
> + return (hi << 16) | lo;
> +}
> +
> +static void
> +ar8xxx_mii_write(struct ar8xxx_priv *priv, int reg, u32 val)
> +{
> + struct mii_bus *bus = priv->mii_bus;
> + u16 r1, r2, r3;
> + u16 lo, hi;
> +
> + split_addr((u32) reg, &r1, &r2, &r3);
> + lo = val & 0xffff;
> + hi = (u16) (val >> 16);
> +
> + mutex_lock(&bus->mdio_lock);
> +
> + bus->write(bus, 0x18, 0, r3);
> + usleep_range(1000, 2000); /* wait for the page switch to propagate */
> + if (priv->mii_lo_first) {
> + bus->write(bus, 0x10 | r2, r1, lo);
> + bus->write(bus, 0x10 | r2, r1 + 1, hi);
> + } else {
> + bus->write(bus, 0x10 | r2, r1 + 1, hi);
> + bus->write(bus, 0x10 | r2, r1, lo);
> + }
> +
> + mutex_unlock(&bus->mdio_lock);
> +}
> +
> +static int
> +_isisc_mdio_reg_get(u32 reg_addr,
> + u8 value[], u32 value_len)
> +{
> + u32 reg_val;
> +
> + if (value_len != sizeof (u32))
> + return -3;
> +
> + reg_val = ar8xxx_mii_read(ext_priv, reg_addr);
> + memcpy(value, ®_val, sizeof (u32));
> +
> + return 0;
> +}
> +
> +static int
> +_isisc_mdio_reg_set(u32 reg_addr, u8 value[], u32 value_len)
> +{
> + u32 reg_val;
> +
> + if (value_len != sizeof (u32))
> + return -3;
> +
> + memcpy(®_val, value, sizeof (u32));
> +
> + ar8xxx_mii_write(ext_priv, reg_addr, reg_val);
> +
> + return 0;
> +}
> +
> +int
> +isisc_reg_get(u32 reg_addr, u8 value[],
> + u32 value_len)
> +{
> + int rv;
> + unsigned long flags;
> +
> + local_irq_save(flags);
> + rv = _isisc_mdio_reg_get(reg_addr, value, value_len);
> + local_irq_restore(flags);
> +
> + return rv;
> +}
> +
> +int
> +isisc_reg_set(u32 reg_addr, u8 value[], u32 value_len)
> +{
> + int rv;
> + unsigned long flags;
> +
> + u32 rt_value = 0;
> +
> + /*get MODULE_EN reg rsv */
> + if (isisc_reg_get(0x30,(void *)&rt_value,4) != 0)
> + return -1;
> +
> + local_irq_save(flags);
> + rv = _isisc_mdio_reg_set(reg_addr, value, value_len);
> + local_irq_restore(flags);
> +
> + return rv;
> +}
> +
> +int
> +isisc_reg_field_get(u32 reg_addr,
> + u32 bit_offset, u32 field_len,
> + u8 value[], u32 value_len)
> +{
> + u32 reg_val = 0;
> +
> + if ((bit_offset >= 32 || (field_len > 32)) || (field_len == 0))
> + return -4;
> +
> + if (value_len != sizeof (u32))
> + return -3;
> +
> + if (isisc_reg_get(reg_addr, (u8 *) & reg_val, sizeof (u32)) != 0)
> + return -1;
> +
> + if(32 == field_len) {
> + *((u32 *) value) = reg_val;
> + } else {
> + *((u32 *) value) = SW_REG_2_FIELD(reg_val, bit_offset, field_len);
> + }
> +
> + return 0;
> +}
> +
> +int
> +isisc_reg_field_set(u32 reg_addr,
> + u32 bit_offset, u32 field_len,
> + const u8 value[], u32 value_len)
> +{
> + u32 reg_val;
> + u32 field_val = *((u32 *) value);
> +
> + if ((bit_offset >= 32 || (field_len > 32)) || (field_len == 0))
> + return -4;
> +
> + if (value_len != sizeof (u32))
> + return -3;
> +
> + if (isisc_reg_get(reg_addr, (u8 *) & reg_val, sizeof (u32)) != 0)
> + return -1;
> +
> + if(32 == field_len) {
> + reg_val = field_val;
> + } else {
> + SW_REG_SET_BY_FIELD_U32(reg_val, field_val, bit_offset, field_len);
> + }
> +
> + if (isisc_reg_set(reg_addr, (u8 *) & reg_val, sizeof (u32)) != 0)
> + return -1;
> +
> + return 0;
> +}
> +
> +#define HSL_REG_ENTRY_GET(rv, reg, index, value, val_len) \
> + do { \
> + rv = hsl_api_table.reg_get(reg##_OFFSET + ((u32)index) *
> reg##_E_OFFSET,\
> + (u8*)value, (u8)val_len); \
> + } while (0);
> +
> +#define HSL_REG_ENTRY_SET(rv, reg, index, value, val_len) \
> + do { \
> + rv = hsl_api_table.reg_set (reg##_OFFSET + ((u32)index) *
> reg##_E_OFFSET,\
> + (u8*)value, (u8)val_len); \
> + } while (0);
> +
> +
> +static void
> +_isisc_ingress_bs_byte_sw_to_hw(u32 sw_bs, u32 * hw_bs)
> +{
> + u32 i;
> + u32 data[8] = {
> + 0, 4 * 1024, 32 * 1024, 128 * 1024, 512 * 1024, 2 * 1024 * 1024,
> + 8 * 1024 * 1024, 32 * 1024 * 1024
> + };
> +
> + for (i = 7; i >= 0; i--) {
> + if (sw_bs >= data[i]) {
> + *hw_bs = i;
> + break;
> + }
> + }
> +
> + return;
> +}
> +
> +static void
> +_isisc_ingress_bs_byte_hw_to_sw(u32 hw_bs, u32 * sw_bs)
> +{
> + u32 data[8] = {
> + 0, 4 * 1024, 32 * 1024, 128 * 1024, 512 * 1024, 2 * 1024 * 1024,
> + 8 * 1024 * 1024, 32 * 1024 * 1024
> + };
> +
> + *sw_bs = data[hw_bs & 0x7];
> +
> + return;
> +}
> +
> +static void
> +_isisc_ingress_bs_frame_sw_to_hw(u32 sw_bs, u32 * hw_bs)
> +{
> + u32 data[8] = { 0, 4, 16, 64, 256, 1024, 4096, 16384 };
> + u32 i;
> +
> + for (i = 7; i >= 0; i--) {
> + if (sw_bs >= data[i]) {
> + *hw_bs = i;
> + break;
> + }
> + }
> +
> + return;
> +}
> +
> +static void
> +_isisc_ingress_bs_frame_hw_to_sw(u32 hw_bs, u32 * sw_bs)
> +{
> + u32 data[8] = { 0, 4, 16, 64, 256, 1024, 4096, 16384 };
> +
> + *sw_bs = data[hw_bs & 0x7];
> +
> + return;
> +}
> +
> +static void
> +_isisc_rate_flag_parse(u32 sw_flag, u32 * hw_flag)
> +{
> + *hw_flag = 0;
> +
> + if (FAL_INGRESS_POLICING_TCP_CTRL & sw_flag) {
> + *hw_flag |= (0x1 << 1);
> + }
> +
> + if (FAL_INGRESS_POLICING_MANAGEMENT & sw_flag) {
> + *hw_flag |= (0x1 << 2);
> + }
> +
> + if (FAL_INGRESS_POLICING_BROAD & sw_flag) {
> + *hw_flag |= (0x1 << 3);
> + }
> +
> + if (FAL_INGRESS_POLICING_UNK_UNI & sw_flag) {
> + *hw_flag |= (0x1 << 4);
> + }
> +
> + if (FAL_INGRESS_POLICING_UNK_MUL & sw_flag) {
> + *hw_flag |= (0x1 << 5);
> + }
> +
> + if (FAL_INGRESS_POLICING_UNI & sw_flag) {
> + *hw_flag |= (0x1 << 6);
> + }
> +
> + if (FAL_INGRESS_POLICING_MUL & sw_flag) {
> + *hw_flag |= (0x1 << 7);
> + }
> +
> + return;
> +}
> +
> +static void
> +_isisc_rate_ts_parse(fal_rate_mt_t sw, u32 * hw)
> +{
> + if (FAL_RATE_MI_100US == sw) {
> + *hw = 0;
> + } else if (FAL_RATE_MI_1MS == sw) {
> + *hw = 1;
> + } else if (FAL_RATE_MI_10MS == sw) {
> + *hw = 2;
> + } else if (FAL_RATE_MI_100MS) {
> + *hw = 3;
> + } else {
> + *hw = 0;
> + }
> +
> + return;
> +}
> +
> +static int
> +isisc_rate_port_policer_set(u32 port_id,
> + fal_port_policer_t * policer)
> +{
> + int rv;
> + u32 cir = 0x7fff, eir = 0x7fff, cbs = 0, ebs = 0, tmp, data[3] = { 0 };
> +
> + data[0] = 0x18000000;
> + if (FAL_BYTE_BASED == policer->meter_unit) {
> + if (true == policer->c_enable) {
> + cir = policer->cir >> 5;
> + policer->cir = cir << 5;
> + _isisc_ingress_bs_byte_sw_to_hw(policer->cbs, &cbs);
> + _isisc_ingress_bs_byte_hw_to_sw(cbs, &(policer->cbs));
> + }
> +
> + if (false == policer->e_enable) {
> + eir = policer->eir >> 5;
> + policer->eir = eir << 5;
> + _isisc_ingress_bs_byte_sw_to_hw(policer->ebs, &ebs);
> + _isisc_ingress_bs_byte_hw_to_sw(ebs, &(policer->ebs));
> + }
> +
> + SW_SET_REG_BY_FIELD(INGRESS_POLICER1, INGRESS_UNIT, 0, data[1]);
> + } else if (FAL_FRAME_BASED == policer->meter_unit) {
> + if (true == policer->c_enable) {
> + cir = (policer->cir * 2) / 125;
> + policer->cir = cir / 2 * 125 + cir % 2 * 63;
> + _isisc_ingress_bs_frame_sw_to_hw(policer->cbs, &cbs);
> + _isisc_ingress_bs_frame_hw_to_sw(cbs, &(policer->cbs));
> + }
> +
> + if (true == policer->c_enable) {
> + eir = (policer->eir * 2) / 125;
> + policer->eir = eir / 2 * 125 + eir % 2 * 63;
> + _isisc_ingress_bs_frame_sw_to_hw(policer->ebs, &ebs);
> + _isisc_ingress_bs_frame_hw_to_sw(ebs, &(policer->ebs));
> + }
> +
> + SW_SET_REG_BY_FIELD(INGRESS_POLICER1, INGRESS_UNIT, 1, data[1]);
> + } else {
> + return -6;
> + }
> +
> + SW_SET_REG_BY_FIELD(INGRESS_POLICER0, INGRESS_CIR, cir, data[0]);
> + SW_SET_REG_BY_FIELD(INGRESS_POLICER0, INGRESS_CBS, cbs, data[0]);
> + SW_SET_REG_BY_FIELD(INGRESS_POLICER1, INGRESS_EIR, eir, data[1]);
> + SW_SET_REG_BY_FIELD(INGRESS_POLICER1, INGRESS_EBS, ebs, data[1]);
> +
> + if (true == policer->combine_mode) {
> + SW_SET_REG_BY_FIELD(INGRESS_POLICER0, RATE_MODE, 1, data[0]);
> + }
> +
> + if (true == policer->deficit_en) {
> + SW_SET_REG_BY_FIELD(INGRESS_POLICER1, INGRESS_BORROW, 1, data[1]);
> + }
> +
> + if (true == policer->color_mode) {
> + SW_SET_REG_BY_FIELD(INGRESS_POLICER1, INGRESS_CM, 1, data[1]);
> + }
> +
> + if (true == policer->couple_flag) {
> + SW_SET_REG_BY_FIELD(INGRESS_POLICER1, INGRESS_CF, 1, data[1]);
> + }
> +
> + _isisc_rate_ts_parse(policer->c_meter_interval, &tmp);
> + SW_SET_REG_BY_FIELD(INGRESS_POLICER0, C_ING_TS, tmp, data[0]);
> +
> + _isisc_rate_ts_parse(policer->e_meter_interval, &tmp);
> + SW_SET_REG_BY_FIELD(INGRESS_POLICER1, E_ING_TS, tmp, data[1]);
> +
> + _isisc_rate_flag_parse(policer->c_rate_flag, &tmp);
> + data[2] = (tmp << 8) & 0xff00;
> +
> + _isisc_rate_flag_parse(policer->e_rate_flag, &tmp);
> + data[2] |= (tmp & 0xff);
> +
> + HSL_REG_ENTRY_SET(rv, INGRESS_POLICER0, port_id,
> + (u8 *) (&data[0]), sizeof (u32));
> +
> + HSL_REG_ENTRY_SET(rv, INGRESS_POLICER1, port_id,
> + (u8 *) (&data[1]), sizeof (u32));
> +
> + HSL_REG_ENTRY_SET(rv, INGRESS_POLICER2, port_id,
> + (u8 *) (&data[2]), sizeof (u32));
> +
> + return rv;
> +}
> +
> +int isisc_rate_init()
> +{
> + hsl_api_table.rate_port_policer_set = isisc_rate_port_policer_set;
> +
> + return 0;
> +}
> +
> +int isisc_init()
> +{
> + hsl_api_table.reg_get = isisc_reg_get;
> + hsl_api_table.reg_set = isisc_reg_set;
> + hsl_api_table.reg_field_get = isisc_reg_field_get;
> + hsl_api_table.reg_field_set = isisc_reg_field_set;
> +
> + isisc_rate_init();
> +
> + return 0;
> +}
> +
> +int fal_init(u8 chip_type)
> +{
> + int rv = 0;
> +
> + memset(&hsl_api_table, 0, sizeof (hsl_api_t));
> +
> + ar8xxx_chip_version = chip_type;
> +
> + switch (ar8xxx_chip_version) {
> + case 0x13:
> + rv = isisc_init();
> + break;
> +
> + default:
> + return -2;
> + }
> +
> + return rv;
> +}
> +
> +int ar8216_ext_init(struct ar8xxx_priv *priv)
> +{
> + ext_priv = priv;
> + fal_init(priv->chip_ver);
> +
> + return 0;
> +}
> +
> +/*fal api for externel calling*/
> +int
> +fal_rate_port_policer_set(u32 port_id,
> + fal_port_policer_t * policer)
> +{
> + int rv;
> +
> + if (NULL == hsl_api_table.rate_port_policer_set)
> + return -26;
> +
> + rv = hsl_api_table.rate_port_policer_set(port_id, policer);
> +
> + return rv;
> +}
> +
> +#define EXT_CMD_STR_MAX 128
> +#define EXT_CMD_WORDS_MAX 16
> +#define EXT_CMD_WORDS_LENGTH_MAX 32
> +/*
> + at cmd_str [in]
> + at cmd_words [out]
> +*/
> +static int
> +ext_cmd_words_get(char *cmd_str,
> + char cmd_words[EXT_CMD_WORDS_MAX][EXT_CMD_WORDS_LENGTH_MAX])
> +{
> + int nr = 0;
> + char *s = cmd_str, *tok;
> + char delim[] = " ";
> +
> + for (tok = strsep(&s, delim); tok; tok = strsep(&s, delim)) {
> + strcpy(cmd_words[nr], tok);
> + nr++;
> + }
> +
> + return nr;
> +}
> +
> +/*
> + at cmd_str [in]
> + at val [out]
> +*/
> +static int
> +ext_cmd_uint32_get(char *cmd_word, unsigned int *val)
> +{
> + int rv=0;
> +
> + if (NULL == cmd_word) {
> + return -EINVAL;
> + }
> +
> + if (0 == cmd_word[0]) {
> + return -EINVAL;
> + }
> +
> + if (cmd_word[0] == '0' && (cmd_word[1] == 'x' || cmd_word[1] == 'X')) {
> + rv = sscanf(cmd_word, "%x", val);
> + } else {
> + rv = sscanf(cmd_word, "%d", val);
> + }
> +
> + if (1 != rv) {
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +#define to_ar8xxx(_dev) container_of(_dev, struct ar8xxx_priv, dev)
> +void
> +ar8xxx_sw_ext_help()
> +{
> + printk("ext cmd help:\n");
> + printk("\"port_rate_limit set port <port_id> rate <rate(Kpbs)>\"\n");
> +}
> +
> +static int
> +ar8xxx_sw_port_rate_limit(struct switch_dev *dev,
> + char cmd_words[EXT_CMD_WORDS_MAX][EXT_CMD_WORDS_LENGTH_MAX])
> +{
> + int port = 0;
> + u32 rate = 0;
> + fal_port_policer_t port_policer;
> +
> + if (NULL == cmd_words[1])
> + return -EINVAL;
> +
> + if (0 == strcmp(cmd_words[1], "set")) {
> + /*port_rate_limit get/set port <port_it> rate <rate(Kpbs)>*/
> + /* 0 1 2 3 4
> 5*/
> + if (NULL == cmd_words[2] || NULL == cmd_words[3] ||
> + NULL == cmd_words[4] ||NULL == cmd_words[5])
> + return -EINVAL;
> + if (0 != strcmp(cmd_words[2], "port"))
> + return -EINVAL;
> + if (0 != strcmp(cmd_words[4], "rate"))
> + return -EINVAL;
> +
> + if ((-EINVAL) == ext_cmd_uint32_get(cmd_words[3], &port))
> + return -EINVAL;
> + if ((-EINVAL) == ext_cmd_uint32_get(cmd_words[5], &rate))
> + return -EINVAL;
> +
> + port_policer.combine_mode = 0;
> + port_policer.meter_unit= 0;
> + port_policer.couple_flag = 0;
> + port_policer.color_mode = 0;
> + port_policer.deficit_en = 0;
> + port_policer.c_enable = 1;
> + port_policer.cir = (rate/32)*32;
> + port_policer.cbs = 32768; //32kbps
> + port_policer.c_rate_flag = 0xfe;
> + port_policer.c_meter_interval = 1; // 1ms
> + port_policer.e_enable = 0;
> + port_policer.eir = 0;
> + port_policer.ebs = 0;
> + port_policer.e_rate_flag = 0;
> + port_policer.e_meter_interval = 1;
> + fal_rate_port_policer_set(port, &port_policer);
> + } else {
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +int
> +ar8xxx_sw_set_extension_switch(struct switch_dev *dev,
> + const struct switch_attr *attr,
> + struct switch_val *val)
> +{
> + int ret = 0;
> + char cmd_str[EXT_CMD_STR_MAX] = {0};
> + char cmd_words[EXT_CMD_WORDS_MAX][EXT_CMD_WORDS_LENGTH_MAX] = {0};
> + struct ar8xxx_priv *priv = to_ar8xxx(dev);
> +
> + if (NULL == val->value.s)
> + return -EINVAL;
> +
> + if (EXT_CMD_STR_MAX <=strlen(val->value.s)) {
> + return -EINVAL;
> + }
> +
> + strcpy(cmd_str, val->value.s);
> +
> + if (0 == ext_cmd_words_get(cmd_str, cmd_words)) {
> + return -EINVAL;
> + }
> +
> + if (NULL == cmd_words[0] || NULL == cmd_words[1])
> + return -EINVAL;
> +
> + mutex_lock(&priv->reg_mutex);
> + if (0 == strcmp(cmd_words[0], "port_rate_limit")) {
> + ret = ar8xxx_sw_port_rate_limit(dev, cmd_words);
> + } else {
> + ar8xxx_sw_ext_help();
> + }
> + mutex_unlock(&priv->reg_mutex);
> +
> + return ret;
> +}
> +
> diff --git a/target/linux/generic/files/drivers/net/phy/ar8216_ext.h
> b/target/linux/generic/files/drivers/net/phy/ar8216_ext.h
> new file mode 100755
> index 0000000..d560f5a
> --- /dev/null
> +++ b/target/linux/generic/files/drivers/net/phy/ar8216_ext.h
> @@ -0,0 +1,269 @@
> +/*
> + * ar8216_ext.c: AR8216 switch driver enhanced feature
> + *
> + * Copyright (C) 2014 Zou Shunxiang <shunxian at qti.qualcomm.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __AR8216_EXT_H
> +#define __AR8216_EXT_H
> +
> +#define HSL_RW 1
> +#define HSL_RO 0
> +/* INGRESS Policer Register0 */
> +#define INGRESS_POLICER0
> +#define INGRESS_POLICER0_OFFSET 0x0b00
> +#define INGRESS_POLICER0_E_LENGTH 4
> +#define INGRESS_POLICER0_E_OFFSET 0x0010
> +#define INGRESS_POLICER0_NR_E 7
> +
> +#define ADD_RATE_BYTE
> +#define INGRESS_POLICER0_ADD_RATE_BYTE_BOFFSET 24
> +#define INGRESS_POLICER0_ADD_RATE_BYTE_BLEN 8
> +#define INGRESS_POLICER0_ADD_RATE_BYTE_FLAG HSL_RW
> +
> +#define C_ING_TS
> +#define INGRESS_POLICER0_C_ING_TS_BOFFSET 22
> +#define INGRESS_POLICER0_C_ING_TS_BLEN 2
> +#define INGRESS_POLICER0_C_ING_TS_FLAG HSL_RW
> +
> +#define RATE_MODE
> +#define INGRESS_POLICER0_RATE_MODE_BOFFSET 20
> +#define INGRESS_POLICER0_RATE_MODE_BLEN 1
> +#define INGRESS_POLICER0_RATE_MODE_FLAG HSL_RW
> +
> +#define INGRESS_CBS
> +#define INGRESS_POLICER0_INGRESS_CBS_BOFFSET 15
> +#define INGRESS_POLICER0_INGRESS_CBS_BLEN 3
> +#define INGRESS_POLICER0_INGRESS_CBS_FLAG HSL_RW
> +
> +#define INGRESS_CIR
> +#define INGRESS_POLICER0_INGRESS_CIR_BOFFSET 0
> +#define INGRESS_POLICER0_INGRESS_CIR_BLEN 15
> +#define INGRESS_POLICER0_INGRESS_CIR_FLAG HSL_RW
> +
> +
> +/* INGRESS Policer Register1 */
> +#define INGRESS_POLICER1
> +#define INGRESS_POLICER1_OFFSET 0x0b04
> +#define INGRESS_POLICER1_E_LENGTH 4
> +#define INGRESS_POLICER1_E_OFFSET 0x0010
> +#define INGRESS_POLICER1_NR_E 7
> +
> +#define INGRESS_BORROW
> +#define INGRESS_POLICER1_INGRESS_BORROW_BOFFSET 23
> +#define INGRESS_POLICER1_INGRESS_BORROW_BLEN 1
> +#define INGRESS_POLICER1_INGRESS_BORROW_FLAG HSL_RW
> +
> +#define INGRESS_UNIT
> +#define INGRESS_POLICER1_INGRESS_UNIT_BOFFSET 22
> +#define INGRESS_POLICER1_INGRESS_UNIT_BLEN 1
> +#define INGRESS_POLICER1_INGRESS_UNIT_FLAG HSL_RW
> +
> +#define INGRESS_CF
> +#define INGRESS_POLICER1_INGRESS_CF_BOFFSET 21
> +#define INGRESS_POLICER1_INGRESS_CF_BLEN 1
> +#define INGRESS_POLICER1_INGRESS_CF_FLAG HSL_RW
> +
> +#define INGRESS_CM
> +#define INGRESS_POLICER1_INGRESS_CM_BOFFSET 20
> +#define INGRESS_POLICER1_INGRESS_CM_BLEN 1
> +#define INGRESS_POLICER1_INGRESS_CM_FLAG HSL_RW
> +
> +#define E_ING_TS
> +#define INGRESS_POLICER1_E_ING_TS_BOFFSET 18
> +#define INGRESS_POLICER1_E_ING_TS_BLEN 2
> +#define INGRESS_POLICER1_E_ING_TS_FLAG HSL_RW
> +
> +#define INGRESS_EBS
> +#define INGRESS_POLICER1_INGRESS_EBS_BOFFSET 15
> +#define INGRESS_POLICER1_INGRESS_EBS_BLEN 3
> +#define INGRESS_POLICER1_INGRESS_EBS_FLAG HSL_RW
> +
> +#define INGRESS_EIR
> +#define INGRESS_POLICER1_INGRESS_EIR_BOFFSET 0
> +#define INGRESS_POLICER1_INGRESS_EIR_BLEN 15
> +#define INGRESS_POLICER1_INGRESS_EIR_FLAG HSL_RW
> +
> +
> +/* INGRESS Policer Register2 */
> +#define INGRESS_POLICER2
> +#define INGRESS_POLICER2_OFFSET 0x0b08
> +#define INGRESS_POLICER2_E_LENGTH 4
> +#define INGRESS_POLICER2_E_OFFSET 0x0010
> +#define INGRESS_POLICER2_NR_E 7
> +
> +#define C_MUL
> +#define INGRESS_POLICER2_C_MUL_BOFFSET 15
> +#define INGRESS_POLICER2_C_MUL_BLEN 1
> +#define INGRESS_POLICER2_C_UNK_MUL_FLAG HSL_RW
> +
> +#define C_UNI
> +#define INGRESS_POLICER2_C_UNI_BOFFSET 14
> +#define INGRESS_POLICER2_C_UNI_BLEN 1
> +#define INGRESS_POLICER2_C_UNI_FLAG HSL_RW
> +
> +#define C_UNK_MUL
> +#define INGRESS_POLICER2_C_UNK_MUL_BOFFSET 13
> +#define INGRESS_POLICER2_C_UNK_MUL_BLEN 1
> +#define INGRESS_POLICER2_C_UNK_MUL_FLAG HSL_RW
> +
> +#define C_UNK_UNI
> +#define INGRESS_POLICER2_C_UNK_UNI_BOFFSET 12
> +#define INGRESS_POLICER2_C_UNK_UNI_BLEN 1
> +#define INGRESS_POLICER2_C_UNK_UNI_FLAG HSL_RW
> +
> +#define C_BROAD
> +#define INGRESS_POLICER2_C_BROAD_BOFFSET 11
> +#define INGRESS_POLICER2_C_BROAD_BLEN 1
> +#define INGRESS_POLICER2_C_BROAD_FLAG HSL_RW
> +
> +#define C_MANAGE
> +#define INGRESS_POLICER2_C_MANAGC_BOFFSET 10
> +#define INGRESS_POLICER2_C_MANAGC_BLEN 1
> +#define INGRESS_POLICER2_C_MANAGC_FLAG HSL_RW
> +
> +#define C_TCP
> +#define INGRESS_POLICER2_C_TCP_BOFFSET 9
> +#define INGRESS_POLICER2_C_TCP_BLEN 1
> +#define INGRESS_POLICER2_C_TCP_FLAG HSL_RW
> +
> +#define C_MIRR
> +#define INGRESS_POLICER2_C_MIRR_BOFFSET 8
> +#define INGRESS_POLICER2_C_MIRR_BLEN 1
> +#define INGRESS_POLICER2_C_MIRR_FLAG HSL_RW
> +
> +#define E_MUL
> +#define INGRESS_POLICER2_E_MUL_BOFFSET 7
> +#define INGRESS_POLICER2_E_MUL_BLEN 1
> +#define INGRESS_POLICER2_E_UNK_MUL_FLAG HSL_RW
> +
> +#define E_UNI
> +#define INGRESS_POLICER2_E_UNI_BOFFSET 6
> +#define INGRESS_POLICER2_E_UNI_BLEN 1
> +#define INGRESS_POLICER2_E_UNI_FLAG HSL_RW
> +
> +#define E_UNK_MUL
> +#define INGRESS_POLICER2_E_UNK_MUL_BOFFSET 5
> +#define INGRESS_POLICER2_E_UNK_MUL_BLEN 1
> +#define INGRESS_POLICER2_E_UNK_MUL_FLAG HSL_RW
> +
> +#define E_UNK_UNI
> +#define INGRESS_POLICER2_E_UNK_UNI_BOFFSET 4
> +#define INGRESS_POLICER2_E_UNK_UNI_BLEN 1
> +#define INGRESS_POLICER2_E_UNK_UNI_FLAG HSL_RW
> +
> +#define E_BROAD
> +#define INGRESS_POLICER2_E_BROAD_BOFFSET 3
> +#define INGRESS_POLICER2_E_BROAD_BLEN 1
> +#define INGRESS_POLICER2_E_BROAD_FLAG HSL_RW
> +
> +#define E_MANAGE
> +#define INGRESS_POLICER2_E_MANAGE_BOFFSET 2
> +#define INGRESS_POLICER2_E_MANAGE_BLEN 1
> +#define INGRESS_POLICER2_E_MANAGE_FLAG HSL_RW
> +
> +#define E_TCP
> +#define INGRESS_POLICER2_E_TCP_BOFFSET 1
> +#define INGRESS_POLICER2_E_TCP_BLEN 1
> +#define INGRESS_POLICER2_E_TCP_FLAG HSL_RW
> +
> +#define E_MIRR
> +#define INGRESS_POLICER2_E_MIRR_BOFFSET 0
> +#define INGRESS_POLICER2_E_MIRR_BLEN 1
> +#define INGRESS_POLICER2_E_MIRR_FLAG HSL_RW
> +
> +
> +#define FAL_INGRESS_POLICING_TCP_CTRL 0x2
> +#define FAL_INGRESS_POLICING_MANAGEMENT 0x4
> +#define FAL_INGRESS_POLICING_BROAD 0x8
> +#define FAL_INGRESS_POLICING_UNK_UNI 0x10
> +#define FAL_INGRESS_POLICING_UNK_MUL 0x20
> +#define FAL_INGRESS_POLICING_UNI 0x40
> +#define FAL_INGRESS_POLICING_MUL 0x80
> +
> +typedef enum {
> + FAL_BYTE_BASED = 0,
> + FAL_FRAME_BASED,
> + FAL_RATE_MODE_BUTT
> +} fal_traffic_unit_t;
> +
> +typedef enum {
> + FAL_RATE_MI_100US = 0,
> + FAL_RATE_MI_1MS,
> + FAL_RATE_MI_10MS,
> + FAL_RATE_MI_100MS,
> +} fal_rate_mt_t;
> +
> +typedef struct {
> + bool c_enable;
> + bool e_enable;
> + bool combine_mode;
> + fal_traffic_unit_t meter_unit;
> + bool color_mode;
> + bool couple_flag;
> + bool deficit_en;
> + bool cir;
> + bool eir;
> + bool cbs;
> + bool ebs;
> + bool c_rate_flag;
> + bool e_rate_flag;
> + fal_rate_mt_t c_meter_interval;
> + fal_rate_mt_t e_meter_interval;
> +} fal_port_policer_t;
> +
> +
> +typedef int
> + (*hsl_rate_port_policer_set)(u32 port_id,
> + fal_port_policer_t * policer);
> +/* REG */
> +typedef int
> + (*hsl_reg_get) (u32 reg_addr,
> + u8 value[], u32 value_len);
> +
> +typedef int
> + (*hsl_reg_set) (u32 reg_addr,
> + u8 value[], u32 value_len);
> +
> +typedef int
> + (*hsl_reg_field_get) (u32 reg_addr,
> + u32 bit_offset, u32 field_len,
> + u8 value[], u32 value_len);
> +
> +typedef int
> + (*hsl_reg_field_set) (u32 reg_addr,
> + u32 bit_offset, u32 field_len,
> + const u8 value[], u32 value_len);
> +
> +typedef struct {
> + hsl_rate_port_policer_set rate_port_policer_set;
> +
> + /* REG Access */
> + hsl_reg_get reg_get;
> + hsl_reg_set reg_set;
> + hsl_reg_field_get reg_field_get;
> + hsl_reg_field_set reg_field_set;
> +} hsl_api_t;
> +
> +extern int fal_rate_port_policer_set(u32 port_id,
> + fal_port_policer_t * policer);
> +
> +extern int fal_init(u8 chip_type);
> +
> +extern int
> +ar8xxx_sw_set_extension_switch(struct switch_dev *dev,
> + const struct switch_attr *attr,
> + struct switch_val *val);
> +
> +
> +#endif
> diff --git a/target/linux/generic/patches-3.10/724-phy_ar8216.patch
> b/target/linux/generic/patches-3.10/724-phy_ar8216.patch
> old mode 100644
> new mode 100755
> index d3b238e..58c6244
> --- a/target/linux/generic/patches-3.10/724-phy_ar8216.patch
> +++ b/target/linux/generic/patches-3.10/724-phy_ar8216.patch
> @@ -18,7 +18,7 @@
> obj-$(CONFIG_MVSWITCH_PHY) += mvswitch.o
> obj-$(CONFIG_IP17XX_PHY) += ip17xx.o
> obj-$(CONFIG_REALTEK_PHY) += realtek.o
> -+obj-$(CONFIG_AR8216_PHY) += ar8216.o
> ++obj-$(CONFIG_AR8216_PHY) += ar8216.o ar8216_ext.o
> obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o
> obj-$(CONFIG_FIXED_PHY) += fixed.o
> obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o
> diff --git a/target/linux/generic/patches-3.10/725-phy_rtl8306.patch
> b/target/linux/generic/patches-3.10/725-phy_rtl8306.patch
> old mode 100644
> new mode 100755
> index 56899a7..43cbb8a
> --- a/target/linux/generic/patches-3.10/725-phy_rtl8306.patch
> +++ b/target/linux/generic/patches-3.10/725-phy_rtl8306.patch
> @@ -16,7 +16,7 @@
> @@ -22,6 +22,7 @@ obj-$(CONFIG_MVSWITCH_PHY) += mvswitch.o
> obj-$(CONFIG_IP17XX_PHY) += ip17xx.o
> obj-$(CONFIG_REALTEK_PHY) += realtek.o
> - obj-$(CONFIG_AR8216_PHY) += ar8216.o
> + obj-$(CONFIG_AR8216_PHY) += ar8216.o ar8216_ext.o
> +obj-$(CONFIG_RTL8306_PHY) += rtl8306.o
> obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o
> obj-$(CONFIG_FIXED_PHY) += fixed.o
> diff --git a/target/linux/generic/patches-3.10/726-phy_rtl8366.patch
> b/target/linux/generic/patches-3.10/726-phy_rtl8366.patch
> old mode 100644
> new mode 100755
> index cbb5ce1..d190a38
> --- a/target/linux/generic/patches-3.10/726-phy_rtl8366.patch
> +++ b/target/linux/generic/patches-3.10/726-phy_rtl8366.patch
> @@ -35,7 +35,7 @@
> +++ b/drivers/net/phy/Makefile
> @@ -23,6 +23,9 @@ obj-$(CONFIG_IP17XX_PHY) += ip17xx.o
> obj-$(CONFIG_REALTEK_PHY) += realtek.o
> - obj-$(CONFIG_AR8216_PHY) += ar8216.o
> + obj-$(CONFIG_AR8216_PHY) += ar8216.o ar8216_ext.o
> obj-$(CONFIG_RTL8306_PHY) += rtl8306.o
> +obj-$(CONFIG_RTL8366_SMI) += rtl8366_smi.o
> +obj-$(CONFIG_RTL8366S_PHY) += rtl8366s.o
> diff --git a/target/linux/generic/patches-3.14/724-phy_ar8216.patch
> b/target/linux/generic/patches-3.14/724-phy_ar8216.patch
> old mode 100644
> new mode 100755
> index d3b238e..58c6244
> --- a/target/linux/generic/patches-3.14/724-phy_ar8216.patch
> +++ b/target/linux/generic/patches-3.14/724-phy_ar8216.patch
> @@ -18,7 +18,7 @@
> obj-$(CONFIG_MVSWITCH_PHY) += mvswitch.o
> obj-$(CONFIG_IP17XX_PHY) += ip17xx.o
> obj-$(CONFIG_REALTEK_PHY) += realtek.o
> -+obj-$(CONFIG_AR8216_PHY) += ar8216.o
> ++obj-$(CONFIG_AR8216_PHY) += ar8216.o ar8216_ext.o
> obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o
> obj-$(CONFIG_FIXED_PHY) += fixed.o
> obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o
> --
> 1.8.2.1
>
>
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel
More information about the openwrt-devel
mailing list