0001
0002
0003
0004
0005
0006 #include <linux/module.h>
0007 #include <linux/kernel.h>
0008 #include <linux/init.h>
0009 #include <linux/netdevice.h>
0010 #include <linux/skbuff.h>
0011
0012 #include <net/ncsi.h>
0013 #include <net/net_namespace.h>
0014 #include <net/sock.h>
0015
0016 #include "internal.h"
0017 #include "ncsi-pkt.h"
0018
0019 static int ncsi_validate_aen_pkt(struct ncsi_aen_pkt_hdr *h,
0020 const unsigned short payload)
0021 {
0022 u32 checksum;
0023 __be32 *pchecksum;
0024
0025 if (h->common.revision != NCSI_PKT_REVISION)
0026 return -EINVAL;
0027 if (ntohs(h->common.length) != payload)
0028 return -EINVAL;
0029
0030
0031
0032
0033
0034 pchecksum = (__be32 *)((void *)(h + 1) + payload - 4);
0035 if (ntohl(*pchecksum) == 0)
0036 return 0;
0037
0038 checksum = ncsi_calculate_checksum((unsigned char *)h,
0039 sizeof(*h) + payload - 4);
0040 if (*pchecksum != htonl(checksum))
0041 return -EINVAL;
0042
0043 return 0;
0044 }
0045
0046 static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
0047 struct ncsi_aen_pkt_hdr *h)
0048 {
0049 struct ncsi_channel *nc, *tmp;
0050 struct ncsi_channel_mode *ncm;
0051 unsigned long old_data, data;
0052 struct ncsi_aen_lsc_pkt *lsc;
0053 struct ncsi_package *np;
0054 bool had_link, has_link;
0055 unsigned long flags;
0056 bool chained;
0057 int state;
0058
0059
0060 ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
0061 if (!nc)
0062 return -ENODEV;
0063
0064
0065 lsc = (struct ncsi_aen_lsc_pkt *)h;
0066
0067 spin_lock_irqsave(&nc->lock, flags);
0068 ncm = &nc->modes[NCSI_MODE_LINK];
0069 old_data = ncm->data[2];
0070 data = ntohl(lsc->status);
0071 ncm->data[2] = data;
0072 ncm->data[4] = ntohl(lsc->oem_status);
0073
0074 had_link = !!(old_data & 0x1);
0075 has_link = !!(data & 0x1);
0076
0077 netdev_dbg(ndp->ndev.dev, "NCSI: LSC AEN - channel %u state %s\n",
0078 nc->id, data & 0x1 ? "up" : "down");
0079
0080 chained = !list_empty(&nc->link);
0081 state = nc->state;
0082 spin_unlock_irqrestore(&nc->lock, flags);
0083
0084 if (state == NCSI_CHANNEL_INACTIVE)
0085 netdev_warn(ndp->ndev.dev,
0086 "NCSI: Inactive channel %u received AEN!\n",
0087 nc->id);
0088
0089 if ((had_link == has_link) || chained)
0090 return 0;
0091
0092 if (!ndp->multi_package && !nc->package->multi_channel) {
0093 if (had_link) {
0094 ndp->flags |= NCSI_DEV_RESHUFFLE;
0095 ncsi_stop_channel_monitor(nc);
0096 spin_lock_irqsave(&ndp->lock, flags);
0097 list_add_tail_rcu(&nc->link, &ndp->channel_queue);
0098 spin_unlock_irqrestore(&ndp->lock, flags);
0099 return ncsi_process_next_channel(ndp);
0100 }
0101
0102 return 0;
0103 }
0104
0105 if (had_link) {
0106 ncm = &nc->modes[NCSI_MODE_TX_ENABLE];
0107 if (ncsi_channel_is_last(ndp, nc)) {
0108
0109 return ncsi_reset_dev(&ndp->ndev);
0110 } else if (ncm->enable) {
0111
0112 ncsi_update_tx_channel(ndp, nc->package, nc, NULL);
0113 }
0114 } else if (has_link && nc->package->preferred_channel == nc) {
0115
0116 ncsi_update_tx_channel(ndp, nc->package, NULL, nc);
0117 } else if (has_link) {
0118 NCSI_FOR_EACH_PACKAGE(ndp, np) {
0119 NCSI_FOR_EACH_CHANNEL(np, tmp) {
0120
0121
0122
0123 ncm = &tmp->modes[NCSI_MODE_TX_ENABLE];
0124 if (ncm->enable &&
0125 !ncsi_channel_has_link(tmp)) {
0126 ncsi_update_tx_channel(ndp, nc->package,
0127 tmp, nc);
0128 break;
0129 }
0130 }
0131 }
0132 }
0133
0134
0135
0136
0137 return 0;
0138 }
0139
0140 static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp,
0141 struct ncsi_aen_pkt_hdr *h)
0142 {
0143 struct ncsi_channel *nc;
0144 unsigned long flags;
0145
0146
0147 ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
0148 if (!nc)
0149 return -ENODEV;
0150
0151 spin_lock_irqsave(&nc->lock, flags);
0152 if (!list_empty(&nc->link) ||
0153 nc->state != NCSI_CHANNEL_ACTIVE) {
0154 spin_unlock_irqrestore(&nc->lock, flags);
0155 return 0;
0156 }
0157 spin_unlock_irqrestore(&nc->lock, flags);
0158
0159 ncsi_stop_channel_monitor(nc);
0160 spin_lock_irqsave(&nc->lock, flags);
0161 nc->state = NCSI_CHANNEL_INVISIBLE;
0162 spin_unlock_irqrestore(&nc->lock, flags);
0163
0164 spin_lock_irqsave(&ndp->lock, flags);
0165 nc->state = NCSI_CHANNEL_INACTIVE;
0166 list_add_tail_rcu(&nc->link, &ndp->channel_queue);
0167 spin_unlock_irqrestore(&ndp->lock, flags);
0168
0169 return ncsi_process_next_channel(ndp);
0170 }
0171
0172 static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv *ndp,
0173 struct ncsi_aen_pkt_hdr *h)
0174 {
0175 struct ncsi_channel *nc;
0176 struct ncsi_channel_mode *ncm;
0177 struct ncsi_aen_hncdsc_pkt *hncdsc;
0178 unsigned long flags;
0179
0180
0181 ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
0182 if (!nc)
0183 return -ENODEV;
0184
0185 spin_lock_irqsave(&nc->lock, flags);
0186 ncm = &nc->modes[NCSI_MODE_LINK];
0187 hncdsc = (struct ncsi_aen_hncdsc_pkt *)h;
0188 ncm->data[3] = ntohl(hncdsc->status);
0189 spin_unlock_irqrestore(&nc->lock, flags);
0190 netdev_dbg(ndp->ndev.dev,
0191 "NCSI: host driver %srunning on channel %u\n",
0192 ncm->data[3] & 0x1 ? "" : "not ", nc->id);
0193
0194 return 0;
0195 }
0196
0197 static struct ncsi_aen_handler {
0198 unsigned char type;
0199 int payload;
0200 int (*handler)(struct ncsi_dev_priv *ndp,
0201 struct ncsi_aen_pkt_hdr *h);
0202 } ncsi_aen_handlers[] = {
0203 { NCSI_PKT_AEN_LSC, 12, ncsi_aen_handler_lsc },
0204 { NCSI_PKT_AEN_CR, 4, ncsi_aen_handler_cr },
0205 { NCSI_PKT_AEN_HNCDSC, 8, ncsi_aen_handler_hncdsc }
0206 };
0207
0208 int ncsi_aen_handler(struct ncsi_dev_priv *ndp, struct sk_buff *skb)
0209 {
0210 struct ncsi_aen_pkt_hdr *h;
0211 struct ncsi_aen_handler *nah = NULL;
0212 int i, ret;
0213
0214
0215 h = (struct ncsi_aen_pkt_hdr *)skb_network_header(skb);
0216 for (i = 0; i < ARRAY_SIZE(ncsi_aen_handlers); i++) {
0217 if (ncsi_aen_handlers[i].type == h->type) {
0218 nah = &ncsi_aen_handlers[i];
0219 break;
0220 }
0221 }
0222
0223 if (!nah) {
0224 netdev_warn(ndp->ndev.dev, "Invalid AEN (0x%x) received\n",
0225 h->type);
0226 return -ENOENT;
0227 }
0228
0229 ret = ncsi_validate_aen_pkt(h, nah->payload);
0230 if (ret) {
0231 netdev_warn(ndp->ndev.dev,
0232 "NCSI: 'bad' packet ignored for AEN type 0x%x\n",
0233 h->type);
0234 goto out;
0235 }
0236
0237 ret = nah->handler(ndp, h);
0238 if (ret)
0239 netdev_err(ndp->ndev.dev,
0240 "NCSI: Handler for AEN type 0x%x returned %d\n",
0241 h->type, ret);
0242 out:
0243 consume_skb(skb);
0244 return ret;
0245 }