Merge branch 'linus' into cont_syslog
[safe/jmp/linux-2.6] / drivers / net / gianfar_ethtool.c
index 562f6c2..9bda023 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/errno.h>
-#include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/delay.h>
@@ -645,6 +644,241 @@ static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
 }
 #endif
 
+static int gfar_ethflow_to_class(int flow_type, u64 *class)
+{
+       switch (flow_type) {
+       case TCP_V4_FLOW:
+               *class = CLASS_CODE_TCP_IPV4;
+               break;
+       case UDP_V4_FLOW:
+               *class = CLASS_CODE_UDP_IPV4;
+               break;
+       case AH_V4_FLOW:
+       case ESP_V4_FLOW:
+               *class = CLASS_CODE_AH_ESP_IPV4;
+               break;
+       case SCTP_V4_FLOW:
+               *class = CLASS_CODE_SCTP_IPV4;
+               break;
+       case TCP_V6_FLOW:
+               *class = CLASS_CODE_TCP_IPV6;
+               break;
+       case UDP_V6_FLOW:
+               *class = CLASS_CODE_UDP_IPV6;
+               break;
+       case AH_V6_FLOW:
+       case ESP_V6_FLOW:
+               *class = CLASS_CODE_AH_ESP_IPV6;
+               break;
+       case SCTP_V6_FLOW:
+               *class = CLASS_CODE_SCTP_IPV6;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow)
+{
+       u32 fcr = 0x0, fpr = FPR_FILER_MASK;
+
+       if (ethflow & RXH_L2DA) {
+               fcr = RQFCR_PID_DAH |RQFCR_CMP_NOMATCH |
+                       RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
+               ftp_rqfpr[priv->cur_filer_idx] = fpr;
+               ftp_rqfcr[priv->cur_filer_idx] = fcr;
+               gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+               priv->cur_filer_idx = priv->cur_filer_idx - 1;
+
+               fcr = RQFCR_PID_DAL | RQFCR_AND | RQFCR_CMP_NOMATCH |
+                               RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
+               ftp_rqfpr[priv->cur_filer_idx] = fpr;
+               ftp_rqfcr[priv->cur_filer_idx] = fcr;
+               gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+               priv->cur_filer_idx = priv->cur_filer_idx - 1;
+       }
+
+       if (ethflow & RXH_VLAN) {
+               fcr = RQFCR_PID_VID | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+                               RQFCR_AND | RQFCR_HASHTBL_0;
+               gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+               ftp_rqfpr[priv->cur_filer_idx] = fpr;
+               ftp_rqfcr[priv->cur_filer_idx] = fcr;
+               priv->cur_filer_idx = priv->cur_filer_idx - 1;
+       }
+
+       if (ethflow & RXH_IP_SRC) {
+               fcr = RQFCR_PID_SIA | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+                       RQFCR_AND | RQFCR_HASHTBL_0;
+               ftp_rqfpr[priv->cur_filer_idx] = fpr;
+               ftp_rqfcr[priv->cur_filer_idx] = fcr;
+               gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+               priv->cur_filer_idx = priv->cur_filer_idx - 1;
+       }
+
+       if (ethflow & (RXH_IP_DST)) {
+               fcr = RQFCR_PID_DIA | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+                       RQFCR_AND | RQFCR_HASHTBL_0;
+               ftp_rqfpr[priv->cur_filer_idx] = fpr;
+               ftp_rqfcr[priv->cur_filer_idx] = fcr;
+               gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+               priv->cur_filer_idx = priv->cur_filer_idx - 1;
+       }
+
+       if (ethflow & RXH_L3_PROTO) {
+               fcr = RQFCR_PID_L4P | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+                       RQFCR_AND | RQFCR_HASHTBL_0;
+               ftp_rqfpr[priv->cur_filer_idx] = fpr;
+               ftp_rqfcr[priv->cur_filer_idx] = fcr;
+               gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+               priv->cur_filer_idx = priv->cur_filer_idx - 1;
+       }
+
+       if (ethflow & RXH_L4_B_0_1) {
+               fcr = RQFCR_PID_SPT | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+                       RQFCR_AND | RQFCR_HASHTBL_0;
+               ftp_rqfpr[priv->cur_filer_idx] = fpr;
+               ftp_rqfcr[priv->cur_filer_idx] = fcr;
+               gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+               priv->cur_filer_idx = priv->cur_filer_idx - 1;
+       }
+
+       if (ethflow & RXH_L4_B_2_3) {
+               fcr = RQFCR_PID_DPT | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+                       RQFCR_AND | RQFCR_HASHTBL_0;
+               ftp_rqfpr[priv->cur_filer_idx] = fpr;
+               ftp_rqfcr[priv->cur_filer_idx] = fcr;
+               gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+               priv->cur_filer_idx = priv->cur_filer_idx - 1;
+       }
+}
+
+static int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow, u64 class)
+{
+       unsigned int last_rule_idx = priv->cur_filer_idx;
+       unsigned int cmp_rqfpr;
+       unsigned int local_rqfpr[MAX_FILER_IDX + 1];
+       unsigned int local_rqfcr[MAX_FILER_IDX + 1];
+       int i = 0x0, k = 0x0;
+       int j = MAX_FILER_IDX, l = 0x0;
+
+       switch (class) {
+       case TCP_V4_FLOW:
+               cmp_rqfpr = RQFPR_IPV4 |RQFPR_TCP;
+               break;
+       case UDP_V4_FLOW:
+               cmp_rqfpr = RQFPR_IPV4 |RQFPR_UDP;
+               break;
+       case TCP_V6_FLOW:
+               cmp_rqfpr = RQFPR_IPV6 |RQFPR_TCP;
+               break;
+       case UDP_V6_FLOW:
+               cmp_rqfpr = RQFPR_IPV6 |RQFPR_UDP;
+               break;
+       case IPV4_FLOW:
+               cmp_rqfpr = RQFPR_IPV4;
+       case IPV6_FLOW:
+               cmp_rqfpr = RQFPR_IPV6;
+               break;
+       default:
+               printk(KERN_ERR "Right now this class is not supported\n");
+               return 0;
+       }
+
+       for (i = 0; i < MAX_FILER_IDX + 1; i++) {
+               local_rqfpr[j] = ftp_rqfpr[i];
+               local_rqfcr[j] = ftp_rqfcr[i];
+               j--;
+               if ((ftp_rqfcr[i] == (RQFCR_PID_PARSE |
+                       RQFCR_CLE |RQFCR_AND)) &&
+                       (ftp_rqfpr[i] == cmp_rqfpr))
+                       break;
+       }
+
+       if (i == MAX_FILER_IDX + 1) {
+               printk(KERN_ERR "No parse rule found, ");
+               printk(KERN_ERR "can't create hash rules\n");
+               return 0;
+       }
+
+       /* If a match was found, then it begins the starting of a cluster rule
+        * if it was already programmed, we need to overwrite these rules
+        */
+       for (l = i+1; l < MAX_FILER_IDX; l++) {
+               if ((ftp_rqfcr[l] & RQFCR_CLE) &&
+                       !(ftp_rqfcr[l] & RQFCR_AND)) {
+                       ftp_rqfcr[l] = RQFCR_CLE | RQFCR_CMP_EXACT |
+                               RQFCR_HASHTBL_0 | RQFCR_PID_MASK;
+                       ftp_rqfpr[l] = FPR_FILER_MASK;
+                       gfar_write_filer(priv, l, ftp_rqfcr[l], ftp_rqfpr[l]);
+                       break;
+               }
+
+               if (!(ftp_rqfcr[l] & RQFCR_CLE) && (ftp_rqfcr[l] & RQFCR_AND))
+                       continue;
+               else {
+                       local_rqfpr[j] = ftp_rqfpr[l];
+                       local_rqfcr[j] = ftp_rqfcr[l];
+                       j--;
+               }
+       }
+
+       priv->cur_filer_idx = l - 1;
+       last_rule_idx = l;
+
+       /* hash rules */
+       ethflow_to_filer_rules(priv, ethflow);
+
+       /* Write back the popped out rules again */
+       for (k = j+1; k < MAX_FILER_IDX; k++) {
+               ftp_rqfpr[priv->cur_filer_idx] = local_rqfpr[k];
+               ftp_rqfcr[priv->cur_filer_idx] = local_rqfcr[k];
+               gfar_write_filer(priv, priv->cur_filer_idx,
+                               local_rqfcr[k], local_rqfpr[k]);
+               if (!priv->cur_filer_idx)
+                       break;
+               priv->cur_filer_idx = priv->cur_filer_idx - 1;
+       }
+
+       return 1;
+}
+
+static int gfar_set_hash_opts(struct gfar_private *priv, struct ethtool_rxnfc *cmd)
+{
+       u64 class;
+
+       if (!gfar_ethflow_to_class(cmd->flow_type, &class))
+               return -EINVAL;
+
+       if (class < CLASS_CODE_USER_PROG1 ||
+                       class > CLASS_CODE_SCTP_IPV6)
+               return -EINVAL;
+
+       /* write the filer rules here */
+       if (!gfar_ethflow_to_filer_table(priv, cmd->data, cmd->flow_type))
+               return -1;
+
+       return 0;
+}
+
+static int gfar_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
+{
+       struct gfar_private *priv = netdev_priv(dev);
+       int ret = 0;
+
+       switch(cmd->cmd) {
+       case ETHTOOL_SRXFH:
+               ret = gfar_set_hash_opts(priv, cmd);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
 const struct ethtool_ops gfar_ethtool_ops = {
        .get_settings = gfar_gsettings,
        .set_settings = gfar_ssettings,
@@ -670,4 +904,5 @@ const struct ethtool_ops gfar_ethtool_ops = {
        .get_wol = gfar_get_wol,
        .set_wol = gfar_set_wol,
 #endif
+       .set_rxnfc = gfar_set_nfc,
 };