/* * 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 #include #include #include #include #include #include //include from rtl8367c dir #include "./rtl8367c/include/rtk_switch.h" #include "./rtl8367c/include/vlan.h" #include "./rtl8367c/include/stat.h" #include "./rtl8367c/include/port.h" #define RTL8367C_SW_CPU_PORT 6 //RTL8367C_PHY_PORT_NUM + ext0 + ext1 #define RTL8367C_NUM_PORTS 7 #define RTL8367C_NUM_VIDS 4096 struct rtl8367_priv { struct switch_dev swdev; bool global_vlan_enable; }; struct rtl8367_mib_counter { const char *name; }; struct rtl8367_vlan_info { unsigned short vid; unsigned int untag; unsigned int member; unsigned char fid; }; struct rtl8367_priv rtl8367_priv_data; unsigned int rtl8367c_port_id[RTL8367C_NUM_PORTS]={0,1,2,3,4,EXT_PORT1,EXT_PORT0}; void (*rtl8367_switch_reset_func)(void)=NULL; static struct rtl8367_mib_counter rtl8367c_mib_counters[] = { {"ifInOctets"}, {"dot3StatsFCSErrors"}, {"dot3StatsSymbolErrors"}, {"dot3InPauseFrames"}, {"dot3ControlInUnknownOpcodes"}, {"etherStatsFragments"}, {"etherStatsJabbers"}, {"ifInUcastPkts"}, {"etherStatsDropEvents"}, {"etherStatsOctets"}, {"etherStatsUndersizePkts"}, {"etherStatsOversizePkts"}, {"etherStatsPkts64Octets"}, {"etherStatsPkts65to127Octets"}, {"etherStatsPkts128to255Octets"}, {"etherStatsPkts256to511Octets"}, {"etherStatsPkts512to1023Octets"}, {"etherStatsPkts1024toMaxOctets"}, {"etherStatsMcastPkts"}, {"etherStatsBcastPkts"}, {"ifOutOctets"}, {"dot3StatsSingleCollisionFrames"}, {"dot3StatsMultipleCollisionFrames"}, {"dot3StatsDeferredTransmissions"}, {"dot3StatsLateCollisions"}, {"etherStatsCollisions"}, {"dot3StatsExcessiveCollisions"}, {"dot3OutPauseFrames"}, {"dot1dBasePortDelayExceededDiscards"}, {"dot1dTpPortInDiscards"}, {"ifOutUcastPkts"}, {"ifOutMulticastPkts"}, {"ifOutBrocastPkts"}, {"outOampduPkts"}, {"inOampduPkts"}, {"pktgenPkts"}, {"inMldChecksumError"}, {"inIgmpChecksumError"}, {"inMldSpecificQuery"}, {"inMldGeneralQuery"}, {"inIgmpSpecificQuery"}, {"inIgmpGeneralQuery"}, {"inMldLeaves"}, {"inIgmpLeaves"}, {"inIgmpJoinsSuccess"}, {"inIgmpJoinsFail"}, {"inMldJoinsSuccess"}, {"inMldJoinsFail"}, {"inReportSuppressionDrop"}, {"inLeaveSuppressionDrop"}, {"outIgmpReports"}, {"outIgmpLeaves"}, {"outIgmpGeneralQuery"}, {"outIgmpSpecificQuery"}, {"outMldReports"}, {"outMldLeaves"}, {"outMldGeneralQuery"}, {"outMldSpecificQuery"}, {"inKnownMulticastPkts"}, {"ifInMulticastPkts"}, {"ifInBroadcastPkts"}, {"ifOutDiscards"} }; /*rtl8367c proprietary switch API wrapper */ static inline unsigned int rtl8367c_sw_to_phy_port(int port) { return rtl8367c_port_id[port]; } static inline unsigned int rtl8367c_portmask_phy_to_sw(rtk_portmask_t phy_portmask) { int i; for (i = 0; i < RTL8367C_NUM_PORTS; i++) { if(RTK_PORTMASK_IS_PORT_SET(phy_portmask,rtl8367c_sw_to_phy_port(i))) { RTK_PORTMASK_PORT_CLEAR(phy_portmask,rtl8367c_sw_to_phy_port(i)); RTK_PORTMASK_PORT_SET(phy_portmask,i); } } return (unsigned int)phy_portmask.bits[0]; } static int rtl8367c_reset_mibs(void) { return rtk_stat_global_reset(); } static int rtl8367c_reset_port_mibs(int port) { return rtk_stat_port_reset(rtl8367c_sw_to_phy_port(port)); } static int rtl8367c_get_mibs_num(void) { return ARRAY_SIZE(rtl8367c_mib_counters); } static const char *rtl8367c_get_mib_name(int idx) { return rtl8367c_mib_counters[idx].name; } static int rtl8367c_get_port_mib_counter(int idx, int port, unsigned long long *counter) { return rtk_stat_port_get(rtl8367c_sw_to_phy_port(port), idx, counter); } static int rtl8367c_is_vlan_valid(unsigned int vlan) { unsigned max = RTL8367C_NUM_VIDS; if (vlan == 0 || vlan >= max) return 0; return 1; } static int rtl8367c_get_vlan( unsigned short vid, struct rtl8367_vlan_info *vlan) { rtk_vlan_cfg_t vlan_cfg; memset(vlan, '\0', sizeof(struct rtl8367_vlan_info)); if (vid >= RTL8367C_NUM_VIDS) return -EINVAL; if(rtk_vlan_get(vid,&vlan_cfg)) return -EINVAL; vlan->vid = vid; vlan->member = rtl8367c_portmask_phy_to_sw(vlan_cfg.mbr); vlan->untag = rtl8367c_portmask_phy_to_sw(vlan_cfg.untag); vlan->fid = vlan_cfg.fid_msti; return 0; } static int rtl8367c_set_vlan( unsigned short vid, u32 mbr, u32 untag, u8 fid) { rtk_vlan_cfg_t vlan_cfg; int i; memset(&vlan_cfg, 0x00, sizeof(rtk_vlan_cfg_t)); for (i = 0; i < RTL8367C_NUM_PORTS; i++) { if (mbr & (1 << i)) { RTK_PORTMASK_PORT_SET(vlan_cfg.mbr, rtl8367c_sw_to_phy_port(i)); if(untag & (1 << i)) RTK_PORTMASK_PORT_SET(vlan_cfg.untag, rtl8367c_sw_to_phy_port(i)); } } vlan_cfg.fid_msti=fid; vlan_cfg.ivl_en = 1; return rtk_vlan_set(vid, &vlan_cfg); } static int rtl8367c_get_pvid( int port, int *pvid) { u32 prio=0; if (port >= RTL8367C_NUM_PORTS) return -EINVAL; return rtk_vlan_portPvid_get(rtl8367c_sw_to_phy_port(port),pvid,&prio); } static int rtl8367c_set_pvid( int port, int pvid) { u32 prio=0; if (port >= RTL8367C_NUM_PORTS) return -EINVAL; return rtk_vlan_portPvid_set(rtl8367c_sw_to_phy_port(port),pvid,prio); } static int rtl8367c_get_port_link(int port, int *link, int *speed, int *duplex) { if(rtk_port_phyStatus_get(rtl8367c_sw_to_phy_port(port),(rtk_port_linkStatus_t *)link, (rtk_port_speed_t *)speed,(rtk_port_duplex_t *)duplex)) return -EINVAL; return 0; } /*common rtl8367 swconfig entry API*/ static int rtl8367_sw_set_vlan_enable(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { struct rtl8367_priv *priv = container_of(dev, struct rtl8367_priv, swdev); priv->global_vlan_enable = val->value.i ; return 0; } static int rtl8367_sw_get_vlan_enable(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { struct rtl8367_priv *priv = container_of(dev, struct rtl8367_priv, swdev); val->value.i = priv->global_vlan_enable; return 0; } static int rtl8367_sw_reset_mibs(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { return rtl8367c_reset_mibs(); } static int rtl8367_sw_reset_port_mibs(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { int port; port = val->port_vlan; if (port >= RTL8367C_NUM_PORTS) return -EINVAL; return rtl8367c_reset_port_mibs(port); } static int rtl8367_sw_get_port_mib(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { int i, len = 0; unsigned long long counter = 0; static char mib_buf[4096]; if (val->port_vlan >= RTL8367C_NUM_PORTS) return -EINVAL; len += snprintf(mib_buf + len, sizeof(mib_buf) - len, "Port %d MIB counters\n", val->port_vlan); for (i = 0; i port_vlan, &counter)) len += snprintf(mib_buf + len, sizeof(mib_buf) - len, "%llu\n", counter); else len += snprintf(mib_buf + len, sizeof(mib_buf) - len, "%s\n", "N/A"); } val->value.s = mib_buf; val->len = len; return 0; } static int rtl8367_sw_get_vlan_info(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { int i; u32 len = 0; struct rtl8367_vlan_info vlan; static char vlan_buf[256]; int err; if (!rtl8367c_is_vlan_valid(val->port_vlan)) return -EINVAL; memset(vlan_buf, '\0', sizeof(vlan_buf)); err = rtl8367c_get_vlan(val->port_vlan, &vlan); if (err) return err; len += snprintf(vlan_buf + len, sizeof(vlan_buf) - len, "VLAN %d: Ports: '", vlan.vid); for (i = 0; i value.s = vlan_buf; val->len = len; return 0; } static int rtl8367_sw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val) { struct switch_port *port; struct rtl8367_vlan_info vlan; int i; if (!rtl8367c_is_vlan_valid(val->port_vlan)) return -EINVAL; if(rtl8367c_get_vlan(val->port_vlan, &vlan)) return -EINVAL; port = &val->value.ports[0]; val->len = 0; for (i = 0; i id = i; port->flags = (vlan.untag & BIT(i)) ? 0 : BIT(SWITCH_PORT_FLAG_TAGGED); val->len++; port++; } return 0; } static int rtl8367_sw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val) { struct switch_port *port; u32 member = 0; u32 untag = 0; u8 fid=0; int err; int i; if (!rtl8367c_is_vlan_valid(val->port_vlan)) return -EINVAL; port = &val->value.ports[0]; for (i = 0; i < val->len; i++, port++) { int pvid = 0; member |= BIT(port->id); if (!(port->flags & BIT(SWITCH_PORT_FLAG_TAGGED))) untag |= BIT(port->id); /* * To ensure that we have a valid MC entry for this VLAN, * initialize the port VLAN ID here. */ err = rtl8367c_get_pvid(port->id, &pvid); if (err < 0) return err; if (pvid == 0) { err = rtl8367c_set_pvid(port->id, val->port_vlan); if (err < 0) return err; } } //pr_info("[%s] vid=%d , mem=%x,untag=%x,fid=%d \n",__func__,val->port_vlan,member,untag,fid); return rtl8367c_set_vlan(val->port_vlan, member, untag, fid); } static int rtl8367_sw_get_port_pvid(struct switch_dev *dev, int port, int *val) { return rtl8367c_get_pvid(port, val); } static int rtl8367_sw_set_port_pvid(struct switch_dev *dev, int port, int val) { return rtl8367c_set_pvid(port, val); } static int rtl8367_sw_reset_switch(struct switch_dev *dev) { if(rtl8367_switch_reset_func) (*rtl8367_switch_reset_func)(); else printk("rest switch is not supported\n"); return 0; } static int rtl8367_sw_get_port_link(struct switch_dev *dev, int port, struct switch_port_link *link) { int speed; if (port >= RTL8367C_NUM_PORTS) return -EINVAL; if(rtl8367c_get_port_link(port,(int *)&link->link,(int *)&speed,(int *)&link->duplex)) return -EINVAL; if (!link->link) return 0; switch (speed) { case 0: link->speed = SWITCH_PORT_SPEED_10; break; case 1: link->speed = SWITCH_PORT_SPEED_100; break; case 2: link->speed = SWITCH_PORT_SPEED_1000; break; default: link->speed = SWITCH_PORT_SPEED_UNKNOWN; break; } return 0; } static struct switch_attr rtl8367_globals[] = { { .type = SWITCH_TYPE_INT, .name = "enable_vlan", .description = "Enable VLAN mode", .set = rtl8367_sw_set_vlan_enable, .get = rtl8367_sw_get_vlan_enable, .max = 1, }, { .type = SWITCH_TYPE_NOVAL, .name = "reset_mibs", .description = "Reset all MIB counters", .set = rtl8367_sw_reset_mibs, } }; static struct switch_attr rtl8367_port[] = { { .type = SWITCH_TYPE_NOVAL, .name = "reset_mib", .description = "Reset single port MIB counters", .set = rtl8367_sw_reset_port_mibs, }, { .type = SWITCH_TYPE_STRING, .name = "mib", .description = "Get MIB counters for port", //.max = 33, .set = NULL, .get = rtl8367_sw_get_port_mib, }, }; static struct switch_attr rtl8367_vlan[] = { { .type = SWITCH_TYPE_STRING, .name = "info", .description = "Get vlan information", .max = 1, .set = NULL, .get = rtl8367_sw_get_vlan_info, }, }; static const struct switch_dev_ops rtl8367_sw_ops = { .attr_global = { .attr = rtl8367_globals, .n_attr = ARRAY_SIZE(rtl8367_globals), }, .attr_port = { .attr = rtl8367_port, .n_attr = ARRAY_SIZE(rtl8367_port), }, .attr_vlan = { .attr = rtl8367_vlan, .n_attr = ARRAY_SIZE(rtl8367_vlan), }, .get_vlan_ports = rtl8367_sw_get_vlan_ports, .set_vlan_ports = rtl8367_sw_set_vlan_ports, .get_port_pvid = rtl8367_sw_get_port_pvid, .set_port_pvid = rtl8367_sw_set_port_pvid, .reset_switch = rtl8367_sw_reset_switch, .get_port_link = rtl8367_sw_get_port_link, }; int rtl8367s_swconfig_init(void (*reset_func)(void)) { struct rtl8367_priv *priv = &rtl8367_priv_data; struct switch_dev *dev=&priv->swdev; int err=0; rtl8367_switch_reset_func = reset_func ; memset(priv, 0, sizeof(struct rtl8367_priv)); priv->global_vlan_enable =0; dev->name = "RTL8367C"; dev->cpu_port = RTL8367C_SW_CPU_PORT; dev->ports = RTL8367C_NUM_PORTS; dev->vlans = RTL8367C_NUM_VIDS; dev->ops = &rtl8367_sw_ops; dev->alias = "RTL8367C"; err = register_switch(dev, NULL); pr_info("[%s]\n",__func__); return err; }