您的位置:首页 > 理论基础 > 计算机网络

OK335xS 网络连接打印信息 hacking

2016-02-02 09:16 766 查看
/***********************************************************************
*                 OK335xS 网络连接打印信息 hacking
* 说明:
*     当我们插入网线的时候,经常会看到对应的网卡已连接,当前属于10M、
* 100M网卡工作状态等等信息,那么这些信息是如何被输出的,工作机制是什么,
* 网卡的速度是由phy决定的还是由mac决定的,是不是在phy对应的中断里处理,
* 等等,这些内容都需要去确认。
*
*                                      2016-2-2 深圳 南山平山村 曾剑锋
**********************************************************************/

/**
* 参考文章:
*     Linux PHY几个状态的跟踪
*         http://www.latelee.org/programming-under-linux/linux-phy-state.html */

static const struct dev_pm_ops cpsw_pm_ops = {   <------+
.suspend    = cpsw_suspend,                         |
.resume        = cpsw_resume,                -----------+
};                                                      |   |
|   |
static struct platform_driver cpsw_driver = {       <---|-+ |
.driver = {                                         | | |
.name     = "cpsw",                             | | |
.owner     = THIS_MODULE,                       | | |
.pm     = &cpsw_pm_ops,               ----------+ | |
},                                                    | |
.probe = cpsw_probe,                                  | |
.remove = __devexit_p(cpsw_remove),                   | |
};                                                        | |
| |
static int __init cpsw_init(void)                         | |
{                                                         | |
return platform_driver_register(&cpsw_driver);   -----+ |
}                                                           |
late_initcall(cpsw_init);                                   |
|
static void __exit cpsw_exit(void)                          |
{                                                           |
platform_driver_unregister(&cpsw_driver);               |
}                                                           |
module_exit(cpsw_exit);                                     |
|
MODULE_LICENSE("GPL");                                      |
MODULE_DESCRIPTION("TI CPSW Ethernet driver");              |
|
|
|
static int cpsw_resume(struct device *dev)       <----------+
{
struct platform_device    *pdev = to_platform_device(dev);
struct net_device    *ndev = platform_get_drvdata(pdev);

pm_runtime_get_sync(&pdev->dev);
cpsw_start_slaves_interface(ndev); ----+
|
return 0;                              |
}                                          |
|
V
static inline void cpsw_start_slaves_interface(struct net_device *ndev)
{
struct cpsw_priv *priv = netdev_priv(ndev);
u32 i = 0;

for (i = 0; i < priv->data.slaves; i++) {
ndev = cpsw_get_slave_ndev(priv, i);
if (netif_running(ndev))
cpsw_ndo_open(ndev);           ----------+
}                                                |
}                                                    |
|
|
static int cpsw_ndo_open(struct net_device *ndev) <--+
{
struct cpsw_priv *priv = netdev_priv(ndev);
int i, ret;
u32 reg;

if (!cpsw_common_res_usage_state(priv))
cpsw_intr_disable(priv);
netif_carrier_off(ndev);

if (priv->data.phy_control)
(*priv->data.phy_control)(true);

reg = __raw_readl(&priv->regs->id_ver);
priv->version = reg;

msg(info, ifup, "initializing cpsw version %d.%d (%d)\n",
(reg >> 8 & 0x7), reg & 0xff, (reg >> 11) & 0x1f);

/* initialize host and slave ports */
if (!cpsw_common_res_usage_state(priv))
cpsw_init_host_port(priv);
for_each_slave(priv, cpsw_slave_open, priv);          -----------+
|
/* Add default VLAN */                                           |
cpsw_add_default_vlan(priv);                                     |
|
if (!cpsw_common_res_usage_state(priv)) {                        |
ret = device_create_file(&ndev->dev, &dev_attr_hw_stats);    |
if (ret < 0) {                                               |
dev_err(priv->dev, "unable to add device attr\n");       |
return ret;                                              |
}                                                            |
|
/* setup tx dma to fixed prio and zero offset */             |
cpdma_control_set(priv->dma, CPDMA_TX_PRIO_FIXED, 1);        |
cpdma_control_set(priv->dma, CPDMA_RX_BUFFER_OFFSET, 0);     |
|
/* disable priority elevation and enable statistics */       |
/* on all ports */                                           |
writel(0, &priv->regs->ptype);                               |
writel(0x7, &priv->regs->stat_port_en);                      |
|
if (WARN_ON(!priv->data.rx_descs))                           |
priv->data.rx_descs = 128;                               |
|
for (i = 0; i < priv->data.rx_descs; i++) {                  |
struct sk_buff *skb;                                     |
|
ret = -ENOMEM;                                           |
skb = netdev_alloc_skb_ip_align(priv->ndev,              |
priv->rx_packet_max);                    |
if (!skb)                                                |
break;                                               |
ret = cpdma_chan_submit(priv->rxch, skb, skb->data,      |
skb_tailroom(skb), 0, GFP_KERNEL);               |
if (WARN_ON(ret < 0)) {                                  |
dev_kfree_skb_any(skb);                              |
break;                                               |
}                                                        |
}                                                            |
/*                                                           |
* continue even if we didn't manage to submit all           |
* receive descs                                             |
*/                                                          |
msg(info, ifup, "submitted %d rx descriptors\n", i);         |
|
if (cpts_register(&priv->pdev->dev, priv->cpts,              |
priv->data.cpts_clock_mult,                        |
priv->data.cpts_clock_shift))                      |
dev_err(priv->dev, "error registering cpts device\n");   |
}                                                                |
|
/* Enable Interrupt pacing if configured */                      |
if (priv->coal_intvl != 0) {                                     |
struct ethtool_coalesce coal;                                |
|
coal.rx_coalesce_usecs = (priv->coal_intvl << 4);            |
cpsw_set_coalesce(ndev, &coal);                              |
}                                                                |
|
/* Enable Timer for capturing cpsw rx interrupts */              |
omap_dm_timer_set_int_enable(dmtimer_rx, OMAP_TIMER_INT_CAPTURE);|
omap_dm_timer_set_capture(dmtimer_rx, 1, 0, 0);                  |
omap_dm_timer_enable(dmtimer_rx);                                |
|
/* Enable Timer for capturing cpsw tx interrupts */              |
omap_dm_timer_set_int_enable(dmtimer_tx, OMAP_TIMER_INT_CAPTURE);|
omap_dm_timer_set_capture(dmtimer_tx, 1, 0, 0);                  |
omap_dm_timer_enable(dmtimer_tx);                                |
|
cpdma_ctlr_start(priv->dma);                                     |
cpsw_intr_enable(priv);                                          |
napi_enable(&priv->napi);                                        |
cpdma_ctlr_eoi(priv->dma);                                       |
|
cpsw_update_slave_open_state(priv, true)                         |
|
return 0;        +-----------------------------------------------+
}                    |
|
V
static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
{
char name[32];
u32 slave_port;

sprintf(name, "slave-%d", slave->slave_num);

soft_reset(name, &slave->sliver->soft_reset);

/* setup priority mapping */
writel(0x76543210, &slave->sliver->rx_pri_map);
if (priv->version == CPSW_VERSION1)
slave_write(slave, 0x33221100, CPSW1_TX_PRI_MAP);
else
slave_write(slave, 0x33221100, CPSW2_TX_PRI_MAP);

/* setup max packet size, and mac address */
__raw_writel(priv->rx_packet_max, &slave->sliver->rx_maxlen);
cpsw_set_slave_mac(slave, priv);

slave->mac_control = 0;    /* no link yet */

slave_port = cpsw_get_slave_port(priv, slave->slave_num);

cpsw_add_dual_emac_mode_default_ale_entries(priv, slave, slave_port);
cpsw_add_switch_mode_bcast_ale_entries(priv, slave_port);
priv->port_state[slave_port] = ALE_PORT_STATE_FORWARD;

slave->phy = phy_connect(priv->ndev, slave->data->phy_id,   ------+
&cpsw_adjust_link, 0, slave->data->phy_if);    ------*--------+
if (IS_ERR(slave->phy)) {                                         |        |
msg(err, ifup, "phy %s not found on slave %d\n",              |        |
slave->data->phy_id, slave->slave_num);                   |        |
slave->phy = NULL;                                            |        |
} else {                                                          |        |
dev_info(priv->dev, "CPSW phy found : id is : 0x%x\n",        |        |
slave->phy->phy_id);                                      |        |
cpsw_set_phy_config(priv, slave->phy);                        |        |
phy_start(slave->phy);                                        |        |
}                     +-------------------------------------------+        |
}                         |                                                    |
|                                                    |
V                                                    |
struct phy_device * phy_connect(struct net_device *dev, const char *bus_id,    |
void (*handler)(struct net_device *), u32 flags,                       |
phy_interface_t interface)                                             |
{                                                                              |
struct phy_device *phydev;                                                 |
struct device *d;                                                          |
int rc;                                                                    |
|
/* Search the list of PHY devices on the mdio bus for the                  |
* PHY with the requested name */                                          |
d = bus_find_device_by_name(&mdio_bus_type, NULL, bus_id);                 |
if (!d) {                                                                  |
pr_err("PHY %s not found\n", bus_id);                                  |
return ERR_PTR(-ENODEV);                                               |
}                                                                          |
phydev = to_phy_device(d);                                                 |
|
rc = phy_connect_direct(dev, phydev, handler, flags, interface); ----+     |
if (rc)                                                              |     |
return ERR_PTR(rc);                                              |     |
|     |
return phydev;                                                       |     |
}                                                                        |     |
EXPORT_SYMBOL(phy_connect);                                              |     |
+----------------------------------------------------------+     |
V                                                                |
int phy_connect_direct(struct net_device *dev, struct phy_device *phydev,      |
void (*handler)(struct net_device *), u32 flags,                |
phy_interface_t interface)                                      |
{                                                                              |
int rc;                                                                    |
|
rc = phy_attach_direct(dev, phydev, flags, interface);                     |
if (rc)                                                                    |
return rc;                                                             |
|
phy_prepare_link(phydev, handler);         ----------------+               |
phy_start_machine(phydev, NULL);      ---------------------*--------+      |
if (phydev->irq > 0)                                       |        |      |
phy_start_interrupts(phydev);                          |        |      |
|        |      |
return 0;                                                  |        |      |
}                                                              |        |      |
EXPORT_SYMBOL(phy_connect_direct);                             |        |      |
|        |      |
|        |      |
static void phy_prepare_link(struct phy_device *phydev,  <-----+        |      |
void (*handler)(struct net_device *))                           |      |
{                                                                       |      |
phydev->adjust_link = handler;                                      |      |
}                                                                       |      |
|      |
|      |
/**                                                                     |      |
* phy_start_machine - start PHY state machine tracking                 |      |
* @phydev: the phy_device struct                                       |      |
* @handler: callback function for state change notifications           |      |
*                                                                      |      |
* Description: The PHY infrastructure can run a state machine          |      |
*   which tracks whether the PHY is starting up, negotiating,          |      |
*   etc.  This function starts the timer which tracks the state        |      |
*   of the PHY.  If you want to be notified when the state changes,    |      |
*   pass in the callback @handler, otherwise, pass NULL.  If you       |      |
*   want to maintain your own state machine, do not call this          |      |
*   function.                                                          |      |
*/                                                                     |      |
void phy_start_machine(struct phy_device *phydev,        <--------------+      |
void (*handler)(struct net_device *))                                  |
{                                                                              |
phydev->adjust_state = handler;                                            |
|
schedule_delayed_work(&phydev->state_queue, HZ);                           |
}                                                                              |
|
|
static void cpsw_adjust_link(struct net_device *ndev)       <------------------+
{
struct cpsw_priv    *priv = netdev_priv(ndev);
bool            link = false;

for_each_slave(priv, _cpsw_adjust_link, priv, &link);   ----------+
|
if (link) {                                                       |
netif_carrier_on(ndev);                                       |
if (netif_running(ndev))                                      |
netif_wake_queue(ndev);                                   |
} else {                                                          |
netif_carrier_off(ndev);                                      |
netif_stop_queue(ndev);                                       |
}                                                                 |
}                                                                     |
|
|
static void _cpsw_adjust_link(struct cpsw_slave *slave,      <--------+
struct cpsw_priv *priv, bool *link)
{
struct phy_device    *phy = slave->phy;
u32            mac_control = 0;
u32            slave_port;

if (!phy)
return;

slave_port = cpsw_get_slave_port(priv, slave->slave_num);

if (phy->link) {
/* enable forwarding */
cpsw_ale_control_set(priv->ale, slave_port,
ALE_PORT_STATE, priv->port_state[slave_port]);

mac_control = priv->data.mac_control;
if (phy->speed == 10)
mac_control |= BIT(18); /* In Band mode */
if (phy->speed == 1000) {
mac_control |= BIT(7);    /* Enable gigabit mode */
}
if (phy->speed == 100)
mac_control |= BIT(15);
if (phy->duplex)
mac_control |= BIT(0);    /* FULLDUPLEXEN    */
if (phy->interface == PHY_INTERFACE_MODE_RGMII) /* RGMII */
mac_control |= (BIT(15)|BIT(16));
*link = true;
} else {
cpsw_ale_control_set(priv->ale, slave_port,
ALE_PORT_STATE, ALE_PORT_STATE_DISABLE);
mac_control = 0;
}

if (mac_control != slave->mac_control) {
phy_print_status(phy);                                    ---------------+
__raw_writel(mac_control, &slave->sliver->mac_control);                  |
}                                                                            |
|
slave->mac_control = mac_control;                                            |
}                                                                                |
|
/**                                                                              |
* phy_print_status - Convenience function to print out the current phy status   |
* @phydev: the phy_device struct                                                |
*/                                                                              |
void phy_print_status(struct phy_device *phydev)                   <-------------+
{
pr_info("PHY: %s - Link is %s", dev_name(&phydev->dev),
phydev->link ? "Up" : "Down");
if (phydev->link)
printk(KERN_CONT " - %d/%s", phydev->speed,
DUPLEX_FULL == phydev->duplex ?
"Full" : "Half");

printk(KERN_CONT "\n");
}
EXPORT_SYMBOL(phy_print_status);

/*
* 1. LAN8710A/LAN8710Ai datasheet
*     1. http://ww1.microchip.com/downloads/en/DeviceDoc/8710a.pdf *     2. 1.2 General Description
*         ......
*         The LAN8710A/LAN8710Ai supports communication with an Ethernet MAC via a standard MII (IEEE 802.3u)/RMII interface. It contains a full-duplex 10-BASE-T/100BASE-TX transceiver and supports 10Mbps (10BASE-T) and 100Mbps (100BASE-TX) operation. The LAN8710A/LAN8710Ai implements auto-negotiation to automatically determine the best possible speed and duplex mode of operation. HP Auto-MDIX support allows the use of direct connect or cross-over LAN cables.
*         ......
* 2. 由上面可知,决定网卡的工作速度的是phy自动决定。
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: