Newer
Older
/*
* ethernet.c - file implements library APIs
*
* Copyright (C) 2020 iopsys Software Solutions AB. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <net/if.h>
#include <stdbool.h>
#include <linux/sockios.h>
#include <linux/mii.h>
#include "ethernet.h"
#ifdef IOPSYS_BROADCOM
extern const struct eth_ops bcm_eth_ops;
#else
extern const struct eth_ops ethsw_ops;
#endif
const struct eth_ops *eth_ops[] = {
#ifdef IOPSYS_BROADCOM
&bcm_eth_ops,
#else
ðsw_ops, /* FIXME */
const struct eth_ops *get_eth_driver(const char *ifname)
{
int i;
for (i = 0; i < sizeof(eth_ops)/sizeof(eth_ops[0]); i++)
if (!strncmp(eth_ops[i]->ifname, ifname,
strlen(eth_ops[i]->ifname)))
return eth_ops[i];
return NULL;
}
int eth_up(const char *ifname)
const struct eth_ops *eth = get_eth_driver(ifname);
if (eth && eth->up)
return eth->up(ifname);
return -1;
}
int eth_down(const char *ifname)
const struct eth_ops *eth = get_eth_driver(ifname);
if (eth && eth->down)
return eth->down(ifname);
return -1;
}
int eth_get_link_settings(const char *ifname, struct eth_link *link)
const struct eth_ops *eth = get_eth_driver(ifname);
if (eth && eth->get_link_settings)
return eth->get_link_settings(ifname, link);
return -1;
}
int eth_set_link_settings(const char *ifname, struct eth_link link)
const struct eth_ops *eth = get_eth_driver(ifname);
if (eth && eth->set_link_settings)
return eth->set_link_settings(ifname, link);
return -1;
}
int eth_get_operstate(const char *ifname, ifopstatus_t *s)
return get_ifoperstatus(ifname, s);
}
int eth_set_operstate(const char *ifname, ifopstatus_t s)
{
/* return set_ifoperstatus(ifname, s); */ //TODO
return -1;
}
int eth_get_stats(const char *ifname, struct eth_stats *s)
const struct eth_ops *eth = get_eth_driver(ifname);
if (eth && eth->get_stats)
return eth->get_stats(ifname, s);
return -1;
}
int eth_get_rmon_stats(const char *ifname, struct eth_rmon_stats *tx,
struct eth_rmon_stats *rx)
const struct eth_ops *eth = get_eth_driver(ifname);
if (eth && eth->get_rmon_stats)
return eth->get_rmon_stats(ifname, tx, rx);
return -1;
}
int eth_poweron_phy(const char *ifname, struct eth_phy p)
const struct eth_ops *eth = get_eth_driver(ifname);
if (eth && eth->poweron_phy)
return eth->poweron_phy(ifname, p);
return -1;
}
int eth_poweroff_phy(const char *ifname, struct eth_phy p)
const struct eth_ops *eth = get_eth_driver(ifname);
if (eth && eth->poweroff_phy)
return eth->poweroff_phy(ifname, p);
return -1;
}
int eth_reset_phy(const char *ifname, int phy_id)
const struct eth_ops *eth = get_eth_driver(ifname);
if (eth && eth->reset_phy)
return eth->reset_phy(ifname, phy_id);
return eth_mii_reset_phy(ifname, phy_id);
int eth_get_phy_id(const char *ifname, int port, int *phy_id)
const struct eth_ops *eth = get_eth_driver(ifname);
if (eth && eth->get_phy_id)
return eth->get_phy_id(ifname, port, phy_id);
return eth_mii_get_phy_id(ifname, port, phy_id);
int eth_ioctl(const char *ifname, int cmd, void *in, int len)
{
struct ifreq ifr;
int s;
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0)
return -1;
if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
fprintf(stderr, "SIOCGIFINDEX failed!\n");
close(s);
return -1;
}
if (len && in)
ifr.ifr_data = (caddr_t)in;
if (ioctl(s, cmd, &ifr) < 0) {
close(s);
return -1;
}
close(s);
return 0;
}
int eth_mii_get_phy_id(const char *ifname, int port, int *phy_id)
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
{
struct mii_ioctl_data *mii;
struct ifreq ifr;
int s;
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0)
return -1;
memset(&ifr, 0, sizeof(struct ifreq));
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
fprintf(stderr, "SIOCGIFINDEX failed!\n");
close(s);
return -1;
}
mii = if_mii(&ifr);
memset(mii, 0, sizeof(struct mii_ioctl_data));
mii->val_in = port;
if (ioctl(s, SIOCGMIIPHY, &ifr) < 0) {
fprintf(stderr, "SIOCGMIIPHY failed!\n");
close(s);
return -1;
}
if (phy_id)
*phy_id = mii->phy_id;
//fprintf(stderr, "%s: phy_id = %d\n", ifname, *phy_id);
close(s);
return 0;
}
static int eth_mii_ioctl(const char *ifname, int cmd, int phy_id, int reg,
int in, int *out)
{
struct mii_ioctl_data *mii;
struct ifreq ifr;
int s;
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0)
return -1;
memset(&ifr, 0, sizeof(struct ifreq));
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
fprintf(stderr, "SIOCGIFINDEX failed!\n");
close(s);
return -1;
}
mii = if_mii(&ifr);
memset(mii, 0, sizeof(struct mii_ioctl_data));
//PHYID_2_MII_IOCTL(phy_id, mii);
mii->phy_id = phy_id;
mii->reg_num = reg;
mii->val_in = in;
if (ioctl(s, cmd, &ifr) < 0) {
fprintf(stderr, "MII cmd on %s failed\n", ifr.ifr_name);
close(s);
return -1;
}
if (out)
*out = mii->val_out; // FIXME?
close(s);
return 0;
}
int eth_mii_read(const char *ifname, int phy_id, int reg, int *out)
return eth_mii_ioctl(ifname, SIOCGMIIREG, phy_id, reg, 0, out);
int eth_mii_write(const char *ifname, int phy_id, int reg, int in)
return eth_mii_ioctl(ifname, SIOCGMIIREG, phy_id, reg, in, NULL);
int eth_mii_reset_phy(const char *ifname, int phy_id)
return eth_mii_ioctl(ifname, SIOCSMIIREG, phy_id,
MII_BMCR, BMCR_RESET, NULL);
}