123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * NC-SI protocol configuration
- *
- * Copyright (C) 2019, IBM Corporation.
- */
- #include <common.h>
- #include <malloc.h>
- #include <phy.h>
- #include <net/ncsi.h>
- #include <net/ncsi-pkt.h>
- #include <asm/unaligned.h>
- #define NCSI_PACKAGE_MAX 8
- #define NCSI_CHANNEL_MAX 31
- #define NCSI_PACKAGE_SHIFT 5
- #define NCSI_PACKAGE_INDEX(c) (((c) >> NCSI_PACKAGE_SHIFT) & 0x7)
- #define NCSI_RESERVED_CHANNEL 0x1f
- #define NCSI_CHANNEL_INDEX(c) ((c) & ((1 << NCSI_PACKAGE_SHIFT) - 1))
- #define NCSI_TO_CHANNEL(p, c) (((p) << NCSI_PACKAGE_SHIFT) | (c))
- #define NCSI_PKT_REVISION 0x01
- #define NCSI_CAP_GENERIC_MASK 0x7f
- #define NCSI_CAP_BC_MASK 0x0f
- #define NCSI_CAP_MC_MASK 0x3f
- #define NCSI_CAP_AEN_MASK 0x07
- #define NCSI_CAP_VLAN_MASK 0x07
- static void ncsi_send_ebf(unsigned int np, unsigned int nc);
- static void ncsi_send_ae(unsigned int np, unsigned int nc);
- static void ncsi_send_gls(unsigned int np, unsigned int nc);
- static int ncsi_send_command(unsigned int np, unsigned int nc, unsigned int cmd,
- uchar *payload, int len, bool wait);
- struct ncsi_channel {
- unsigned int id;
- bool has_link;
- /* capabilities */
- u32 cap_generic;
- u32 cap_bc;
- u32 cap_mc;
- u32 cap_buffer;
- u32 cap_aen;
- u32 cap_vlan;
- /* version information */
- struct {
- u32 version; /* Supported BCD encoded NCSI version */
- u32 alpha2; /* Supported BCD encoded NCSI version */
- u8 fw_name[12]; /* Firmware name string */
- u32 fw_version; /* Firmware version */
- u16 pci_ids[4]; /* PCI identification */
- u32 mf_id; /* Manufacture ID */
- } version;
- };
- struct ncsi_package {
- unsigned int id;
- unsigned int n_channels;
- struct ncsi_channel *channels;
- };
- struct ncsi {
- enum {
- NCSI_PROBE_PACKAGE_SP,
- NCSI_PROBE_PACKAGE_DP,
- NCSI_PROBE_CHANNEL_SP,
- NCSI_PROBE_CHANNEL,
- NCSI_CONFIG,
- } state;
- unsigned int pending_requests;
- unsigned int requests[256];
- unsigned int last_request;
- unsigned int current_package;
- unsigned int current_channel;
- unsigned int n_packages;
- struct ncsi_package *packages;
- };
- struct ncsi *ncsi_priv;
- bool ncsi_active(void)
- {
- unsigned int np, nc;
- if (!ncsi_priv)
- return false;
- np = ncsi_priv->current_package;
- nc = ncsi_priv->current_channel;
- if (ncsi_priv->state != NCSI_CONFIG)
- return false;
- return np < NCSI_PACKAGE_MAX && nc < NCSI_CHANNEL_MAX &&
- ncsi_priv->packages[np].channels[nc].has_link;
- }
- static unsigned int cmd_payload(int cmd)
- {
- switch (cmd) {
- case NCSI_PKT_CMD_CIS:
- return 0;
- case NCSI_PKT_CMD_SP:
- return 4;
- case NCSI_PKT_CMD_DP:
- return 0;
- case NCSI_PKT_CMD_EC:
- return 0;
- case NCSI_PKT_CMD_DC:
- return 4;
- case NCSI_PKT_CMD_RC:
- return 4;
- case NCSI_PKT_CMD_ECNT:
- return 0;
- case NCSI_PKT_CMD_DCNT:
- return 0;
- case NCSI_PKT_CMD_AE:
- return 8;
- case NCSI_PKT_CMD_SL:
- return 8;
- case NCSI_PKT_CMD_GLS:
- return 0;
- case NCSI_PKT_CMD_SVF:
- return 8;
- case NCSI_PKT_CMD_EV:
- return 4;
- case NCSI_PKT_CMD_DV:
- return 0;
- case NCSI_PKT_CMD_SMA:
- return 8;
- case NCSI_PKT_CMD_EBF:
- return 4;
- case NCSI_PKT_CMD_DBF:
- return 0;
- case NCSI_PKT_CMD_EGMF:
- return 4;
- case NCSI_PKT_CMD_DGMF:
- return 0;
- case NCSI_PKT_CMD_SNFC:
- return 4;
- case NCSI_PKT_CMD_GVI:
- return 0;
- case NCSI_PKT_CMD_GC:
- return 0;
- case NCSI_PKT_CMD_GP:
- return 0;
- case NCSI_PKT_CMD_GCPS:
- return 0;
- case NCSI_PKT_CMD_GNS:
- return 0;
- case NCSI_PKT_CMD_GNPTS:
- return 0;
- case NCSI_PKT_CMD_GPS:
- return 0;
- default:
- printf("NCSI: Unknown command 0x%02x\n", cmd);
- return 0;
- }
- }
- static u32 ncsi_calculate_checksum(unsigned char *data, int len)
- {
- u32 checksum = 0;
- int i;
- for (i = 0; i < len; i += 2)
- checksum += (((u32)data[i] << 8) | data[i + 1]);
- checksum = (~checksum + 1);
- return checksum;
- }
- static int ncsi_validate_rsp(struct ncsi_rsp_pkt *pkt, int payload)
- {
- struct ncsi_rsp_pkt_hdr *hdr = &pkt->rsp;
- u32 checksum, c_offset;
- __be32 pchecksum;
- if (hdr->common.revision != 1) {
- printf("NCSI: 0x%02x response has unsupported revision 0x%x\n",
- hdr->common.type, hdr->common.revision);
- return -1;
- }
- if (hdr->code != 0) {
- printf("NCSI: 0x%02x response returns error %d\n",
- hdr->common.type, __be16_to_cpu(hdr->code));
- if (ntohs(hdr->reason) == 0x05)
- printf("(Invalid command length)\n");
- return -1;
- }
- if (ntohs(hdr->common.length) != payload) {
- printf("NCSI: 0x%02x response has incorrect length %d\n",
- hdr->common.type, hdr->common.length);
- return -1;
- }
- c_offset = sizeof(struct ncsi_rsp_pkt_hdr) + payload - sizeof(checksum);
- pchecksum = get_unaligned_be32((void *)hdr + c_offset);
- if (pchecksum != 0) {
- checksum = ncsi_calculate_checksum((unsigned char *)hdr,
- c_offset);
- if (pchecksum != checksum) {
- printf("NCSI: 0x%02x response has invalid checksum\n",
- hdr->common.type);
- return -1;
- }
- }
- return 0;
- }
- static void ncsi_rsp_ec(struct ncsi_rsp_pkt *pkt)
- {
- struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
- unsigned int np, nc;
- np = NCSI_PACKAGE_INDEX(rsp->common.channel);
- nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
- if (ncsi_priv->packages[np].channels[nc].cap_aen != 0)
- ncsi_send_ae(np, nc);
- /* else, done */
- }
- static void ncsi_rsp_ecnt(struct ncsi_rsp_pkt *pkt)
- {
- struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
- unsigned int np, nc;
- np = NCSI_PACKAGE_INDEX(rsp->common.channel);
- nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
- ncsi_send_command(np, nc, NCSI_PKT_CMD_EC, NULL, 0, true);
- }
- static void ncsi_rsp_ebf(struct ncsi_rsp_pkt *pkt)
- {
- struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
- unsigned int np, nc;
- np = NCSI_PACKAGE_INDEX(rsp->common.channel);
- nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
- ncsi_send_command(np, nc, NCSI_PKT_CMD_ECNT, NULL, 0, true);
- }
- static void ncsi_rsp_sma(struct ncsi_rsp_pkt *pkt)
- {
- struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
- unsigned int np, nc;
- np = NCSI_PACKAGE_INDEX(rsp->common.channel);
- nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
- ncsi_send_ebf(np, nc);
- }
- static void ncsi_rsp_gc(struct ncsi_rsp_pkt *pkt)
- {
- struct ncsi_rsp_gc_pkt *gc = (struct ncsi_rsp_gc_pkt *)pkt;
- struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&gc->rsp;
- struct ncsi_channel *c;
- unsigned int np, nc;
- np = NCSI_PACKAGE_INDEX(rsp->common.channel);
- nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
- if (np >= ncsi_priv->n_packages ||
- nc >= ncsi_priv->packages[np].n_channels) {
- printf("NCSI: Invalid package / channel (0x%02x, 0x%02x)\n",
- np, nc);
- return;
- }
- c = &ncsi_priv->packages[np].channels[nc];
- c->cap_generic = ntohl(gc->cap) & NCSI_CAP_GENERIC_MASK;
- c->cap_bc = ntohl(gc->bc_cap) & NCSI_CAP_BC_MASK;
- c->cap_mc = ntohl(gc->mc_cap) & NCSI_CAP_MC_MASK;
- c->cap_aen = ntohl(gc->aen_cap) & NCSI_CAP_AEN_MASK;
- c->cap_vlan = ntohl(gc->vlan_mode) & NCSI_CAP_VLAN_MASK;
- /* End of probe for this channel */
- }
- static void ncsi_rsp_gvi(struct ncsi_rsp_pkt *pkt)
- {
- struct ncsi_rsp_gvi_pkt *gvi = (struct ncsi_rsp_gvi_pkt *)pkt;
- struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&gvi->rsp;
- struct ncsi_channel *c;
- unsigned int np, nc, i;
- np = NCSI_PACKAGE_INDEX(rsp->common.channel);
- nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
- if (np >= ncsi_priv->n_packages ||
- nc >= ncsi_priv->packages[np].n_channels) {
- printf("NCSI: Invalid package / channel (0x%02x, 0x%02x)\n",
- np, nc);
- return;
- }
- c = &ncsi_priv->packages[np].channels[nc];
- c->version.version = get_unaligned_be32(&gvi->ncsi_version);
- c->version.alpha2 = gvi->alpha2;
- memcpy(c->version.fw_name, gvi->fw_name, sizeof(c->version.fw_name));
- c->version.fw_version = get_unaligned_be32(&gvi->fw_version);
- for (i = 0; i < ARRAY_SIZE(c->version.pci_ids); i++)
- c->version.pci_ids[i] = get_unaligned_be16(gvi->pci_ids + i);
- c->version.mf_id = get_unaligned_be32(&gvi->mf_id);
- if (ncsi_priv->state == NCSI_PROBE_CHANNEL)
- ncsi_send_command(np, nc, NCSI_PKT_CMD_GC, NULL, 0, true);
- }
- static void ncsi_rsp_gls(struct ncsi_rsp_pkt *pkt)
- {
- struct ncsi_rsp_gls_pkt *gls = (struct ncsi_rsp_gls_pkt *)pkt;
- struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&gls->rsp;
- unsigned int np, nc;
- np = NCSI_PACKAGE_INDEX(rsp->common.channel);
- nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
- if (np >= ncsi_priv->n_packages ||
- nc >= ncsi_priv->packages[np].n_channels) {
- printf("NCSI: Invalid package / channel (0x%02x, 0x%02x)\n",
- np, nc);
- return;
- }
- ncsi_priv->packages[np].channels[nc].has_link =
- !!(get_unaligned_be32(&gls->status));
- if (ncsi_priv->state == NCSI_PROBE_CHANNEL)
- ncsi_send_command(np, nc, NCSI_PKT_CMD_GVI, NULL, 0, true);
- }
- static void ncsi_rsp_cis(struct ncsi_rsp_pkt *pkt)
- {
- struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)pkt;
- struct ncsi_package *package;
- unsigned int np, nc;
- np = NCSI_PACKAGE_INDEX(rsp->common.channel);
- nc = NCSI_CHANNEL_INDEX(rsp->common.channel);
- if (np >= ncsi_priv->n_packages) {
- printf("NCSI: Mystery package 0x%02x from CIS\n", np);
- return;
- }
- package = &ncsi_priv->packages[np];
- if (nc < package->n_channels) {
- /*
- * This is fine in general but in the current design we
- * don't send CIS commands to known channels.
- */
- debug("NCSI: Duplicate channel 0x%02x\n", nc);
- return;
- }
- package->channels = realloc(package->channels,
- sizeof(struct ncsi_channel) *
- (package->n_channels + 1));
- if (!package->channels) {
- printf("NCSI: Could not allocate memory for new channel\n");
- return;
- }
- debug("NCSI: New channel 0x%02x\n", nc);
- package->channels[nc].id = nc;
- package->channels[nc].has_link = false;
- package->n_channels++;
- ncsi_send_gls(np, nc);
- }
- static void ncsi_rsp_dp(struct ncsi_rsp_pkt *pkt)
- {
- struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)pkt;
- unsigned int np;
- /* No action needed */
- np = NCSI_PACKAGE_INDEX(rsp->common.channel);
- if (np >= ncsi_priv->n_packages)
- debug("NCSI: DP response from unknown package %d\n", np);
- }
- static void ncsi_rsp_sp(struct ncsi_rsp_pkt *pkt)
- {
- struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)pkt;
- unsigned int np;
- np = NCSI_PACKAGE_INDEX(rsp->common.channel);
- if (np < ncsi_priv->n_packages) {
- /* Already know about this package */
- debug("NCSI: package 0x%02x selected\n", np);
- return;
- }
- debug("NCSI: adding new package %d\n", np);
- ncsi_priv->packages = realloc(ncsi_priv->packages,
- sizeof(struct ncsi_package) *
- (ncsi_priv->n_packages + 1));
- if (!ncsi_priv->packages) {
- printf("NCSI: could not allocate memory for new package\n");
- return;
- }
- ncsi_priv->packages[np].id = np;
- ncsi_priv->packages[np].n_channels = 0;
- ncsi_priv->packages[np].channels = NULL;
- ncsi_priv->n_packages++;
- }
- static void ncsi_update_state(struct ncsi_rsp_pkt_hdr *nh)
- {
- bool timeout = !nh;
- int np, nc;
- switch (ncsi_priv->state) {
- case NCSI_PROBE_PACKAGE_SP:
- if (!timeout &&
- ncsi_priv->current_package + 1 < NCSI_PACKAGE_MAX) {
- ncsi_priv->current_package++;
- } else {
- ncsi_priv->state = NCSI_PROBE_PACKAGE_DP;
- ncsi_priv->current_package = 0;
- }
- return ncsi_probe_packages();
- case NCSI_PROBE_PACKAGE_DP:
- if (ncsi_priv->current_package + 1 < ncsi_priv->n_packages &&
- !timeout) {
- ncsi_priv->current_package++;
- } else {
- if (!ncsi_priv->n_packages) {
- printf("NCSI: no packages found\n");
- net_set_state(NETLOOP_FAIL);
- return;
- }
- printf("NCSI: probing channels\n");
- ncsi_priv->state = NCSI_PROBE_CHANNEL_SP;
- ncsi_priv->current_package = 0;
- ncsi_priv->current_channel = 0;
- }
- return ncsi_probe_packages();
- case NCSI_PROBE_CHANNEL_SP:
- if (!timeout && nh->common.type == NCSI_PKT_RSP_SP) {
- ncsi_priv->state = NCSI_PROBE_CHANNEL;
- return ncsi_probe_packages();
- }
- printf("NCSI: failed to select package 0x%0x2 or timeout\n",
- ncsi_priv->current_package);
- net_set_state(NETLOOP_FAIL);
- break;
- case NCSI_PROBE_CHANNEL:
- // TODO only does package 0 for now
- if (ncsi_priv->pending_requests == 0) {
- np = ncsi_priv->current_package;
- nc = ncsi_priv->current_channel;
- /* Configure first channel that has link */
- if (ncsi_priv->packages[np].channels[nc].has_link) {
- ncsi_priv->state = NCSI_CONFIG;
- } else if (ncsi_priv->current_channel + 1 <
- NCSI_CHANNEL_MAX) {
- ncsi_priv->current_channel++;
- } else {
- // XXX As above only package 0
- printf("NCSI: no channel found with link\n");
- net_set_state(NETLOOP_FAIL);
- return;
- }
- return ncsi_probe_packages();
- }
- break;
- case NCSI_CONFIG:
- if (ncsi_priv->pending_requests == 0) {
- printf("NCSI: configuration done!\n");
- net_set_state(NETLOOP_SUCCESS);
- } else if (timeout) {
- printf("NCSI: timeout during configure\n");
- net_set_state(NETLOOP_FAIL);
- }
- break;
- default:
- printf("NCSI: something went very wrong, nevermind\n");
- net_set_state(NETLOOP_FAIL);
- break;
- }
- }
- static void ncsi_timeout_handler(void)
- {
- if (ncsi_priv->pending_requests)
- ncsi_priv->pending_requests--;
- ncsi_update_state(NULL);
- }
- static int ncsi_send_command(unsigned int np, unsigned int nc, unsigned int cmd,
- uchar *payload, int len, bool wait)
- {
- struct ncsi_pkt_hdr *hdr;
- __be32 *pchecksum;
- int eth_hdr_size;
- u32 checksum;
- uchar *pkt, *start;
- int final_len;
- pkt = calloc(1, PKTSIZE_ALIGN + PKTALIGN);
- if (!pkt)
- return -ENOMEM;
- start = pkt;
- eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_NCSI);
- pkt += eth_hdr_size;
- /* Set NCSI command header fields */
- hdr = (struct ncsi_pkt_hdr *)pkt;
- hdr->mc_id = 0;
- hdr->revision = NCSI_PKT_REVISION;
- hdr->id = ++ncsi_priv->last_request;
- ncsi_priv->requests[ncsi_priv->last_request] = 1;
- hdr->type = cmd;
- hdr->channel = NCSI_TO_CHANNEL(np, nc);
- hdr->length = htons(len);
- if (payload && len)
- memcpy(pkt + sizeof(struct ncsi_pkt_hdr), payload, len);
- /* Calculate checksum */
- checksum = ncsi_calculate_checksum((unsigned char *)hdr,
- sizeof(*hdr) + len);
- pchecksum = (__be32 *)((void *)(hdr + 1) + len);
- put_unaligned_be32(htonl(checksum), pchecksum);
- if (wait) {
- net_set_timeout_handler(1000UL, ncsi_timeout_handler);
- ncsi_priv->pending_requests++;
- }
- if (len < 26)
- len = 26;
- /* frame header, packet header, payload, checksum */
- final_len = eth_hdr_size + sizeof(struct ncsi_cmd_pkt_hdr) + len + 4;
- net_send_packet(start, final_len);
- free(start);
- return 0;
- }
- static void ncsi_handle_aen(struct ip_udp_hdr *ip, unsigned int len)
- {
- struct ncsi_aen_pkt_hdr *hdr = (struct ncsi_aen_pkt_hdr *)ip;
- int payload, i;
- __be32 pchecksum;
- u32 checksum;
- switch (hdr->type) {
- case NCSI_PKT_AEN_LSC:
- printf("NCSI: link state changed\n");
- payload = 12;
- break;
- case NCSI_PKT_AEN_CR:
- printf("NCSI: re-configuration required\n");
- payload = 4;
- break;
- case NCSI_PKT_AEN_HNCDSC:
- /* Host notifcation - N/A but weird */
- debug("NCSI: HNCDSC AEN received\n");
- return;
- default:
- printf("%s: Invalid type 0x%02x\n", __func__, hdr->type);
- return;
- }
- /* Validate packet */
- if (hdr->common.revision != 1) {
- printf("NCSI: 0x%02x response has unsupported revision 0x%x\n",
- hdr->common.type, hdr->common.revision);
- return;
- }
- if (ntohs(hdr->common.length) != payload) {
- printf("NCSI: 0x%02x response has incorrect length %d\n",
- hdr->common.type, hdr->common.length);
- return;
- }
- pchecksum = get_unaligned_be32((void *)(hdr + 1) + payload - 4);
- if (pchecksum != 0) {
- checksum = ncsi_calculate_checksum((unsigned char *)hdr,
- sizeof(*hdr) + payload - 4);
- if (pchecksum != checksum) {
- printf("NCSI: 0x%02x response has invalid checksum\n",
- hdr->common.type);
- return;
- }
- }
- /* Link or configuration lost - just redo the discovery process */
- ncsi_priv->state = NCSI_PROBE_PACKAGE_SP;
- for (i = 0; i < ncsi_priv->n_packages; i++)
- free(ncsi_priv->packages[i].channels);
- free(ncsi_priv->packages);
- ncsi_priv->n_packages = 0;
- ncsi_priv->current_package = NCSI_PACKAGE_MAX;
- ncsi_priv->current_channel = NCSI_CHANNEL_MAX;
- ncsi_probe_packages();
- }
- void ncsi_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip,
- unsigned int len)
- {
- struct ncsi_rsp_pkt *pkt = (struct ncsi_rsp_pkt *)ip;
- struct ncsi_rsp_pkt_hdr *nh = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp;
- void (*handler)(struct ncsi_rsp_pkt *pkt) = NULL;
- unsigned short payload;
- if (ncsi_priv->pending_requests)
- ncsi_priv->pending_requests--;
- if (len < sizeof(struct ncsi_rsp_pkt_hdr)) {
- printf("NCSI: undersized packet: %u bytes\n", len);
- goto out;
- }
- if (nh->common.type == NCSI_PKT_AEN)
- return ncsi_handle_aen(ip, len);
- switch (nh->common.type) {
- case NCSI_PKT_RSP_SP:
- payload = 4;
- handler = ncsi_rsp_sp;
- break;
- case NCSI_PKT_RSP_DP:
- payload = 4;
- handler = ncsi_rsp_dp;
- break;
- case NCSI_PKT_RSP_CIS:
- payload = 4;
- handler = ncsi_rsp_cis;
- break;
- case NCSI_PKT_RSP_GLS:
- payload = 16;
- handler = ncsi_rsp_gls;
- break;
- case NCSI_PKT_RSP_GVI:
- payload = 40;
- handler = ncsi_rsp_gvi;
- break;
- case NCSI_PKT_RSP_GC:
- payload = 32;
- handler = ncsi_rsp_gc;
- break;
- case NCSI_PKT_RSP_SMA:
- payload = 4;
- handler = ncsi_rsp_sma;
- break;
- case NCSI_PKT_RSP_EBF:
- payload = 4;
- handler = ncsi_rsp_ebf;
- break;
- case NCSI_PKT_RSP_ECNT:
- payload = 4;
- handler = ncsi_rsp_ecnt;
- break;
- case NCSI_PKT_RSP_EC:
- payload = 4;
- handler = ncsi_rsp_ec;
- break;
- case NCSI_PKT_RSP_AE:
- payload = 4;
- handler = NULL;
- break;
- default:
- printf("NCSI: unsupported packet type 0x%02x\n",
- nh->common.type);
- goto out;
- }
- if (ncsi_validate_rsp(pkt, payload) != 0) {
- printf("NCSI: discarding invalid packet of type 0x%02x\n",
- nh->common.type);
- goto out;
- }
- if (handler)
- handler(pkt);
- out:
- ncsi_update_state(nh);
- }
- static void ncsi_send_sp(unsigned int np)
- {
- uchar payload[4] = {0};
- ncsi_send_command(np, NCSI_RESERVED_CHANNEL, NCSI_PKT_CMD_SP,
- (unsigned char *)&payload,
- cmd_payload(NCSI_PKT_CMD_SP), true);
- }
- static void ncsi_send_dp(unsigned int np)
- {
- ncsi_send_command(np, NCSI_RESERVED_CHANNEL, NCSI_PKT_CMD_DP, NULL, 0,
- true);
- }
- static void ncsi_send_gls(unsigned int np, unsigned int nc)
- {
- ncsi_send_command(np, nc, NCSI_PKT_CMD_GLS, NULL, 0, true);
- }
- static void ncsi_send_cis(unsigned int np, unsigned int nc)
- {
- ncsi_send_command(np, nc, NCSI_PKT_CMD_CIS, NULL, 0, true);
- }
- static void ncsi_send_ae(unsigned int np, unsigned int nc)
- {
- struct ncsi_cmd_ae_pkt cmd;
- memset(&cmd, 0, sizeof(cmd));
- cmd.mode = htonl(ncsi_priv->packages[np].channels[nc].cap_aen);
- ncsi_send_command(np, nc, NCSI_PKT_CMD_AE,
- ((unsigned char *)&cmd)
- + sizeof(struct ncsi_cmd_pkt_hdr),
- cmd_payload(NCSI_PKT_CMD_AE), true);
- }
- static void ncsi_send_ebf(unsigned int np, unsigned int nc)
- {
- struct ncsi_cmd_ebf_pkt cmd;
- memset(&cmd, 0, sizeof(cmd));
- cmd.mode = htonl(ncsi_priv->packages[np].channels[nc].cap_bc);
- ncsi_send_command(np, nc, NCSI_PKT_CMD_EBF,
- ((unsigned char *)&cmd)
- + sizeof(struct ncsi_cmd_pkt_hdr),
- cmd_payload(NCSI_PKT_CMD_EBF), true);
- }
- static void ncsi_send_sma(unsigned int np, unsigned int nc)
- {
- struct ncsi_cmd_sma_pkt cmd;
- unsigned char *addr, i;
- addr = eth_get_ethaddr();
- if (!addr) {
- printf("NCSI: no MAC address configured\n");
- return;
- }
- memset(&cmd, 0, sizeof(cmd));
- for (i = 0; i < ARP_HLEN; i++)
- cmd.mac[i] = addr[i];
- cmd.index = 1;
- cmd.at_e = 1;
- ncsi_send_command(np, nc, NCSI_PKT_CMD_SMA,
- ((unsigned char *)&cmd)
- + sizeof(struct ncsi_cmd_pkt_hdr),
- cmd_payload(NCSI_PKT_CMD_SMA), true);
- }
- void ncsi_probe_packages(void)
- {
- struct ncsi_package *package;
- unsigned int np, nc;
- switch (ncsi_priv->state) {
- case NCSI_PROBE_PACKAGE_SP:
- if (ncsi_priv->current_package == NCSI_PACKAGE_MAX)
- ncsi_priv->current_package = 0;
- ncsi_send_sp(ncsi_priv->current_package);
- break;
- case NCSI_PROBE_PACKAGE_DP:
- ncsi_send_dp(ncsi_priv->current_package);
- break;
- case NCSI_PROBE_CHANNEL_SP:
- if (ncsi_priv->n_packages > 0)
- ncsi_send_sp(ncsi_priv->current_package);
- else
- printf("NCSI: no packages discovered, configuration not possible\n");
- break;
- case NCSI_PROBE_CHANNEL:
- /* Kicks off chain of channel discovery */
- ncsi_send_cis(ncsi_priv->current_package,
- ncsi_priv->current_channel);
- break;
- case NCSI_CONFIG:
- for (np = 0; np < ncsi_priv->n_packages; np++) {
- package = &ncsi_priv->packages[np];
- for (nc = 0; nc < package->n_channels; nc++)
- if (package->channels[nc].has_link)
- break;
- if (nc < package->n_channels)
- break;
- }
- if (np == ncsi_priv->n_packages) {
- printf("NCSI: no link available\n");
- return;
- }
- printf("NCSI: configuring channel %d\n", nc);
- ncsi_priv->current_package = np;
- ncsi_priv->current_channel = nc;
- /* Kicks off rest of configure chain */
- ncsi_send_sma(np, nc);
- break;
- default:
- printf("NCSI: unknown state 0x%x\n", ncsi_priv->state);
- }
- }
- int ncsi_probe(struct phy_device *phydev)
- {
- if (!phydev->priv) {
- phydev->priv = malloc(sizeof(struct ncsi));
- if (!phydev->priv)
- return -ENOMEM;
- memset(phydev->priv, 0, sizeof(struct ncsi));
- }
- ncsi_priv = phydev->priv;
- return 0;
- }
- int ncsi_startup(struct phy_device *phydev)
- {
- /* Set phydev parameters */
- phydev->speed = SPEED_100;
- phydev->duplex = DUPLEX_FULL;
- /* Normal phy reset is N/A */
- phydev->flags |= PHY_FLAG_BROKEN_RESET;
- /* Set initial probe state */
- ncsi_priv->state = NCSI_PROBE_PACKAGE_SP;
- /* No active package/channel yet */
- ncsi_priv->current_package = NCSI_PACKAGE_MAX;
- ncsi_priv->current_channel = NCSI_CHANNEL_MAX;
- /* Pretend link works so the MAC driver sets final bits up */
- phydev->link = true;
- /* Set ncsi_priv so we can use it when called from net_loop() */
- ncsi_priv = phydev->priv;
- return 0;
- }
- int ncsi_shutdown(struct phy_device *phydev)
- {
- printf("NCSI: Disabling package %d\n", ncsi_priv->current_package);
- ncsi_send_dp(ncsi_priv->current_package);
- return 0;
- }
- static struct phy_driver ncsi_driver = {
- .uid = PHY_NCSI_ID,
- .mask = 0xffffffff,
- .name = "NC-SI",
- .features = PHY_100BT_FEATURES | PHY_DEFAULT_FEATURES |
- SUPPORTED_100baseT_Full | SUPPORTED_MII,
- .probe = ncsi_probe,
- .startup = ncsi_startup,
- .shutdown = ncsi_shutdown,
- };
- int phy_ncsi_init(void)
- {
- phy_register(&ncsi_driver);
- return 0;
- }
|