diff options
Diffstat (limited to 'drivers/net/forcedeth.c')
-rw-r--r-- | drivers/net/forcedeth.c | 93 |
1 files changed, 76 insertions, 17 deletions
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index c93f3d08dc42..4ebcd052e150 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c | |||
@@ -81,6 +81,7 @@ | |||
81 | * cause DMA to kfree'd memory. | 81 | * cause DMA to kfree'd memory. |
82 | * 0.31: 14 Nov 2004: ethtool support for getting/setting link | 82 | * 0.31: 14 Nov 2004: ethtool support for getting/setting link |
83 | * capabilities. | 83 | * capabilities. |
84 | * 0.32: 16 Apr 2005: RX_ERROR4 handling added. | ||
84 | * | 85 | * |
85 | * Known bugs: | 86 | * Known bugs: |
86 | * We suspect that on some hardware no TX done interrupts are generated. | 87 | * We suspect that on some hardware no TX done interrupts are generated. |
@@ -92,7 +93,7 @@ | |||
92 | * DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few | 93 | * DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few |
93 | * superfluous timer interrupts from the nic. | 94 | * superfluous timer interrupts from the nic. |
94 | */ | 95 | */ |
95 | #define FORCEDETH_VERSION "0.31" | 96 | #define FORCEDETH_VERSION "0.32" |
96 | #define DRV_NAME "forcedeth" | 97 | #define DRV_NAME "forcedeth" |
97 | 98 | ||
98 | #include <linux/module.h> | 99 | #include <linux/module.h> |
@@ -109,6 +110,7 @@ | |||
109 | #include <linux/mii.h> | 110 | #include <linux/mii.h> |
110 | #include <linux/random.h> | 111 | #include <linux/random.h> |
111 | #include <linux/init.h> | 112 | #include <linux/init.h> |
113 | #include <linux/if_vlan.h> | ||
112 | 114 | ||
113 | #include <asm/irq.h> | 115 | #include <asm/irq.h> |
114 | #include <asm/io.h> | 116 | #include <asm/io.h> |
@@ -1013,6 +1015,59 @@ static void nv_tx_timeout(struct net_device *dev) | |||
1013 | spin_unlock_irq(&np->lock); | 1015 | spin_unlock_irq(&np->lock); |
1014 | } | 1016 | } |
1015 | 1017 | ||
1018 | /* | ||
1019 | * Called when the nic notices a mismatch between the actual data len on the | ||
1020 | * wire and the len indicated in the 802 header | ||
1021 | */ | ||
1022 | static int nv_getlen(struct net_device *dev, void *packet, int datalen) | ||
1023 | { | ||
1024 | int hdrlen; /* length of the 802 header */ | ||
1025 | int protolen; /* length as stored in the proto field */ | ||
1026 | |||
1027 | /* 1) calculate len according to header */ | ||
1028 | if ( ((struct vlan_ethhdr *)packet)->h_vlan_proto == __constant_htons(ETH_P_8021Q)) { | ||
1029 | protolen = ntohs( ((struct vlan_ethhdr *)packet)->h_vlan_encapsulated_proto ); | ||
1030 | hdrlen = VLAN_HLEN; | ||
1031 | } else { | ||
1032 | protolen = ntohs( ((struct ethhdr *)packet)->h_proto); | ||
1033 | hdrlen = ETH_HLEN; | ||
1034 | } | ||
1035 | dprintk(KERN_DEBUG "%s: nv_getlen: datalen %d, protolen %d, hdrlen %d\n", | ||
1036 | dev->name, datalen, protolen, hdrlen); | ||
1037 | if (protolen > ETH_DATA_LEN) | ||
1038 | return datalen; /* Value in proto field not a len, no checks possible */ | ||
1039 | |||
1040 | protolen += hdrlen; | ||
1041 | /* consistency checks: */ | ||
1042 | if (datalen > ETH_ZLEN) { | ||
1043 | if (datalen >= protolen) { | ||
1044 | /* more data on wire than in 802 header, trim of | ||
1045 | * additional data. | ||
1046 | */ | ||
1047 | dprintk(KERN_DEBUG "%s: nv_getlen: accepting %d bytes.\n", | ||
1048 | dev->name, protolen); | ||
1049 | return protolen; | ||
1050 | } else { | ||
1051 | /* less data on wire than mentioned in header. | ||
1052 | * Discard the packet. | ||
1053 | */ | ||
1054 | dprintk(KERN_DEBUG "%s: nv_getlen: discarding long packet.\n", | ||
1055 | dev->name); | ||
1056 | return -1; | ||
1057 | } | ||
1058 | } else { | ||
1059 | /* short packet. Accept only if 802 values are also short */ | ||
1060 | if (protolen > ETH_ZLEN) { | ||
1061 | dprintk(KERN_DEBUG "%s: nv_getlen: discarding short packet.\n", | ||
1062 | dev->name); | ||
1063 | return -1; | ||
1064 | } | ||
1065 | dprintk(KERN_DEBUG "%s: nv_getlen: accepting %d bytes.\n", | ||
1066 | dev->name, datalen); | ||
1067 | return datalen; | ||
1068 | } | ||
1069 | } | ||
1070 | |||
1016 | static void nv_rx_process(struct net_device *dev) | 1071 | static void nv_rx_process(struct net_device *dev) |
1017 | { | 1072 | { |
1018 | struct fe_priv *np = get_nvpriv(dev); | 1073 | struct fe_priv *np = get_nvpriv(dev); |
@@ -1064,7 +1119,7 @@ static void nv_rx_process(struct net_device *dev) | |||
1064 | np->stats.rx_errors++; | 1119 | np->stats.rx_errors++; |
1065 | goto next_pkt; | 1120 | goto next_pkt; |
1066 | } | 1121 | } |
1067 | if (Flags & (NV_RX_ERROR1|NV_RX_ERROR2|NV_RX_ERROR3|NV_RX_ERROR4)) { | 1122 | if (Flags & (NV_RX_ERROR1|NV_RX_ERROR2|NV_RX_ERROR3)) { |
1068 | np->stats.rx_errors++; | 1123 | np->stats.rx_errors++; |
1069 | goto next_pkt; | 1124 | goto next_pkt; |
1070 | } | 1125 | } |
@@ -1078,22 +1133,24 @@ static void nv_rx_process(struct net_device *dev) | |||
1078 | np->stats.rx_errors++; | 1133 | np->stats.rx_errors++; |
1079 | goto next_pkt; | 1134 | goto next_pkt; |
1080 | } | 1135 | } |
1081 | if (Flags & NV_RX_ERROR) { | 1136 | if (Flags & NV_RX_ERROR4) { |
1082 | /* framing errors are soft errors, the rest is fatal. */ | 1137 | len = nv_getlen(dev, np->rx_skbuff[i]->data, len); |
1083 | if (Flags & NV_RX_FRAMINGERR) { | 1138 | if (len < 0) { |
1084 | if (Flags & NV_RX_SUBSTRACT1) { | ||
1085 | len--; | ||
1086 | } | ||
1087 | } else { | ||
1088 | np->stats.rx_errors++; | 1139 | np->stats.rx_errors++; |
1089 | goto next_pkt; | 1140 | goto next_pkt; |
1090 | } | 1141 | } |
1091 | } | 1142 | } |
1143 | /* framing errors are soft errors. */ | ||
1144 | if (Flags & NV_RX_FRAMINGERR) { | ||
1145 | if (Flags & NV_RX_SUBSTRACT1) { | ||
1146 | len--; | ||
1147 | } | ||
1148 | } | ||
1092 | } else { | 1149 | } else { |
1093 | if (!(Flags & NV_RX2_DESCRIPTORVALID)) | 1150 | if (!(Flags & NV_RX2_DESCRIPTORVALID)) |
1094 | goto next_pkt; | 1151 | goto next_pkt; |
1095 | 1152 | ||
1096 | if (Flags & (NV_RX2_ERROR1|NV_RX2_ERROR2|NV_RX2_ERROR3|NV_RX2_ERROR4)) { | 1153 | if (Flags & (NV_RX2_ERROR1|NV_RX2_ERROR2|NV_RX2_ERROR3)) { |
1097 | np->stats.rx_errors++; | 1154 | np->stats.rx_errors++; |
1098 | goto next_pkt; | 1155 | goto next_pkt; |
1099 | } | 1156 | } |
@@ -1107,17 +1164,19 @@ static void nv_rx_process(struct net_device *dev) | |||
1107 | np->stats.rx_errors++; | 1164 | np->stats.rx_errors++; |
1108 | goto next_pkt; | 1165 | goto next_pkt; |
1109 | } | 1166 | } |
1110 | if (Flags & NV_RX2_ERROR) { | 1167 | if (Flags & NV_RX2_ERROR4) { |
1111 | /* framing errors are soft errors, the rest is fatal. */ | 1168 | len = nv_getlen(dev, np->rx_skbuff[i]->data, len); |
1112 | if (Flags & NV_RX2_FRAMINGERR) { | 1169 | if (len < 0) { |
1113 | if (Flags & NV_RX2_SUBSTRACT1) { | ||
1114 | len--; | ||
1115 | } | ||
1116 | } else { | ||
1117 | np->stats.rx_errors++; | 1170 | np->stats.rx_errors++; |
1118 | goto next_pkt; | 1171 | goto next_pkt; |
1119 | } | 1172 | } |
1120 | } | 1173 | } |
1174 | /* framing errors are soft errors */ | ||
1175 | if (Flags & NV_RX2_FRAMINGERR) { | ||
1176 | if (Flags & NV_RX2_SUBSTRACT1) { | ||
1177 | len--; | ||
1178 | } | ||
1179 | } | ||
1121 | Flags &= NV_RX2_CHECKSUMMASK; | 1180 | Flags &= NV_RX2_CHECKSUMMASK; |
1122 | if (Flags == NV_RX2_CHECKSUMOK1 || | 1181 | if (Flags == NV_RX2_CHECKSUMOK1 || |
1123 | Flags == NV_RX2_CHECKSUMOK2 || | 1182 | Flags == NV_RX2_CHECKSUMOK2 || |