linux-bk/drivers/net/e1000/e1000_ethtool.c
<<
>>
Prefs
   1/*******************************************************************************
   2
   3  
   4  Copyright(c) 1999 - 2002 Intel Corporation. All rights reserved.
   5  
   6  This program is free software; you can redistribute it and/or modify it 
   7  under the terms of the GNU General Public License as published by the Free 
   8  Software Foundation; either version 2 of the License, or (at your option) 
   9  any later version.
  10  
  11  This program is distributed in the hope that it will be useful, but WITHOUT 
  12  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
  13  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
  14  more details.
  15  
  16  You should have received a copy of the GNU General Public License along with
  17  this program; if not, write to the Free Software Foundation, Inc., 59 
  18  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  19  
  20  The full GNU General Public License is included in this distribution in the
  21  file called LICENSE.
  22  
  23  Contact Information:
  24  Linux NICS <linux.nics@intel.com>
  25  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  26
  27*******************************************************************************/
  28
  29/* ethtool support for e1000 */
  30
  31#include "e1000.h"
  32
  33#include <asm/uaccess.h>
  34
  35extern char e1000_driver_name[];
  36extern char e1000_driver_version[];
  37
  38extern int e1000_up(struct e1000_adapter *adapter);
  39extern void e1000_down(struct e1000_adapter *adapter);
  40extern void e1000_reset(struct e1000_adapter *adapter);
  41
  42static void
  43e1000_ethtool_gset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd)
  44{
  45        struct e1000_hw *hw = &adapter->hw;
  46
  47        if(hw->media_type == e1000_media_type_copper) {
  48
  49                ecmd->supported = (SUPPORTED_10baseT_Half |
  50                                   SUPPORTED_10baseT_Full |
  51                                   SUPPORTED_100baseT_Half |
  52                                   SUPPORTED_100baseT_Full |
  53                                   SUPPORTED_1000baseT_Full|
  54                                   SUPPORTED_Autoneg |
  55                                   SUPPORTED_TP);
  56
  57                ecmd->advertising = ADVERTISED_TP;
  58
  59                if(hw->autoneg == 1) {
  60                        ecmd->advertising |= ADVERTISED_Autoneg;
  61
  62                        /* the e1000 autoneg seems to match ethtool nicely */
  63
  64                        ecmd->advertising |= hw->autoneg_advertised;
  65                }
  66
  67                ecmd->port = PORT_TP;
  68                ecmd->phy_address = hw->phy_addr;
  69
  70                if(hw->mac_type == e1000_82543)
  71                        ecmd->transceiver = XCVR_EXTERNAL;
  72                else
  73                        ecmd->transceiver = XCVR_INTERNAL;
  74
  75        } else {
  76                ecmd->supported   = (SUPPORTED_1000baseT_Full |
  77                                     SUPPORTED_FIBRE |
  78                                     SUPPORTED_Autoneg);
  79
  80                ecmd->advertising = (SUPPORTED_1000baseT_Full |
  81                                     SUPPORTED_FIBRE |
  82                                     SUPPORTED_Autoneg);
  83
  84                ecmd->port = PORT_FIBRE;
  85
  86                if(hw->mac_type >= e1000_82545)
  87                        ecmd->transceiver = XCVR_INTERNAL;
  88                else
  89                        ecmd->transceiver = XCVR_EXTERNAL;
  90        }
  91
  92        if(netif_carrier_ok(adapter->netdev)) {
  93
  94                e1000_get_speed_and_duplex(hw, &adapter->link_speed,
  95                                                   &adapter->link_duplex);
  96                ecmd->speed = adapter->link_speed;
  97
  98                /* unfortunatly FULL_DUPLEX != DUPLEX_FULL
  99                 *          and HALF_DUPLEX != DUPLEX_HALF */
 100
 101                if(adapter->link_duplex == FULL_DUPLEX)
 102                        ecmd->duplex = DUPLEX_FULL;
 103                else
 104                        ecmd->duplex = DUPLEX_HALF;
 105        } else {
 106                ecmd->speed = -1;
 107                ecmd->duplex = -1;
 108        }
 109
 110        ecmd->autoneg = (hw->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE);
 111}
 112
 113static int
 114e1000_ethtool_sset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd)
 115{
 116        struct e1000_hw *hw = &adapter->hw;
 117
 118        if(ecmd->autoneg == AUTONEG_ENABLE) {
 119                hw->autoneg = 1;
 120                hw->autoneg_advertised = (ecmd->advertising & 0x002F);
 121        } else {
 122                hw->autoneg = 0;
 123                switch(ecmd->speed + ecmd->duplex) {
 124                case SPEED_10 + DUPLEX_HALF:
 125                        hw->forced_speed_duplex = e1000_10_half;
 126                        break;
 127                case SPEED_10 + DUPLEX_FULL:
 128                        hw->forced_speed_duplex = e1000_10_full;
 129                        break;
 130                case SPEED_100 + DUPLEX_HALF:
 131                        hw->forced_speed_duplex = e1000_100_half;
 132                        break;
 133                case SPEED_100 + DUPLEX_FULL:
 134                        hw->forced_speed_duplex = e1000_100_full;
 135                        break;
 136                case SPEED_1000 + DUPLEX_FULL:
 137                        hw->autoneg = 1;
 138                        hw->autoneg_advertised = ADVERTISE_1000_FULL;
 139                        break;
 140                case SPEED_1000 + DUPLEX_HALF: /* not supported */
 141                default:
 142                        return -EINVAL;
 143                }
 144        }
 145
 146        /* reset the link */
 147
 148        if(netif_running(adapter->netdev)) {
 149                e1000_down(adapter);
 150                e1000_up(adapter);
 151        } else
 152                e1000_reset(adapter);
 153
 154        return 0;
 155}
 156
 157static inline int
 158e1000_eeprom_size(struct e1000_hw *hw)
 159{
 160        if((hw->mac_type > e1000_82544) &&
 161           (E1000_READ_REG(hw, EECD) & E1000_EECD_SIZE))
 162                return 512;
 163        else
 164                return 128;
 165}
 166
 167static void
 168e1000_ethtool_gdrvinfo(struct e1000_adapter *adapter,
 169                       struct ethtool_drvinfo *drvinfo)
 170{
 171        strncpy(drvinfo->driver,  e1000_driver_name, 32);
 172        strncpy(drvinfo->version, e1000_driver_version, 32);
 173        strncpy(drvinfo->fw_version, "N/A", 32);
 174        strncpy(drvinfo->bus_info, adapter->pdev->slot_name, 32);
 175#define E1000_REGS_LEN 32
 176        drvinfo->regdump_len  = E1000_REGS_LEN * sizeof(uint32_t);
 177        drvinfo->eedump_len  = e1000_eeprom_size(&adapter->hw);
 178}
 179
 180static void
 181e1000_ethtool_gregs(struct e1000_adapter *adapter,
 182                    struct ethtool_regs *regs, uint32_t *regs_buff)
 183{
 184        struct e1000_hw *hw = &adapter->hw;
 185
 186        regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id;
 187
 188        regs_buff[0]  = E1000_READ_REG(hw, CTRL);
 189        regs_buff[1]  = E1000_READ_REG(hw, STATUS);
 190
 191        regs_buff[2]  = E1000_READ_REG(hw, RCTL);
 192        regs_buff[3]  = E1000_READ_REG(hw, RDLEN);
 193        regs_buff[4]  = E1000_READ_REG(hw, RDH);
 194        regs_buff[5]  = E1000_READ_REG(hw, RDT);
 195        regs_buff[6]  = E1000_READ_REG(hw, RDTR);
 196        
 197        regs_buff[7]  = E1000_READ_REG(hw, TCTL);
 198        regs_buff[8]  = E1000_READ_REG(hw, TDLEN);
 199        regs_buff[9]  = E1000_READ_REG(hw, TDH);
 200        regs_buff[10] = E1000_READ_REG(hw, TDT);
 201        regs_buff[11] = E1000_READ_REG(hw, TIDV);
 202
 203        return;
 204}
 205
 206static int
 207e1000_ethtool_geeprom(struct e1000_adapter *adapter,
 208                      struct ethtool_eeprom *eeprom, uint16_t *eeprom_buff)
 209{
 210        struct e1000_hw *hw = &adapter->hw;
 211        int i, max_len, first_word, last_word;
 212
 213        if(eeprom->len == 0) 
 214                return -EINVAL;
 215
 216        eeprom->magic = hw->vendor_id | (hw->device_id << 16);
 217
 218        max_len = e1000_eeprom_size(hw);
 219
 220        if(eeprom->offset > eeprom->offset + eeprom->len)
 221                return -EINVAL;
 222        
 223        if((eeprom->offset + eeprom->len) > max_len)
 224                eeprom->len = (max_len - eeprom->offset);
 225
 226        first_word = eeprom->offset >> 1;
 227        last_word = (eeprom->offset + eeprom->len - 1) >> 1;
 228
 229        for(i = 0; i <= (last_word - first_word); i++)
 230                e1000_read_eeprom(hw, first_word + i, &eeprom_buff[i]);
 231        return 0;
 232}
 233
 234static int 
 235e1000_ethtool_seeprom(struct e1000_adapter *adapter,
 236                      struct ethtool_eeprom *eeprom, void *user_data)
 237{
 238        struct e1000_hw *hw = &adapter->hw;
 239        uint16_t eeprom_buff[256];
 240        int i, max_len, first_word, last_word;
 241        void *ptr;
 242
 243        if(eeprom->len == 0)
 244                return -EOPNOTSUPP;
 245
 246        if(eeprom->magic != (hw->vendor_id | (hw->device_id << 16)))
 247                return -EFAULT;
 248
 249        max_len = e1000_eeprom_size(hw);
 250
 251        if((eeprom->offset + eeprom->len) > max_len)
 252                eeprom->len = (max_len - eeprom->offset);
 253
 254        first_word = eeprom->offset >> 1;
 255        last_word = (eeprom->offset + eeprom->len - 1) >> 1;
 256        ptr = (void *)eeprom_buff;
 257
 258        if(eeprom->offset & 1) {
 259                /* need read/modify/write of first changed EEPROM word */
 260                /* only the second byte of the word is being modified */
 261                e1000_read_eeprom(hw, first_word, &eeprom_buff[0]);
 262                ptr++;
 263        }
 264        if((eeprom->offset + eeprom->len) & 1) {
 265                /* need read/modify/write of last changed EEPROM word */
 266                /* only the first byte of the word is being modified */
 267                e1000_read_eeprom(hw, last_word,
 268                                  &eeprom_buff[last_word - first_word]);
 269        }
 270        if(copy_from_user(ptr, user_data, eeprom->len))
 271                return -EFAULT;
 272
 273        for(i = 0; i <= (last_word - first_word); i++)
 274                e1000_write_eeprom(hw, first_word + i, eeprom_buff[i]);
 275
 276        /* Update the checksum over the first part of the EEPROM if needed */
 277        if(first_word <= EEPROM_CHECKSUM_REG)
 278                e1000_update_eeprom_checksum(hw);
 279
 280        return 0;
 281}
 282
 283static void
 284e1000_ethtool_gwol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol)
 285{
 286        struct e1000_hw *hw = &adapter->hw;
 287
 288        switch(adapter->hw.device_id) {
 289        case E1000_DEV_ID_82542:
 290        case E1000_DEV_ID_82543GC_FIBER:
 291        case E1000_DEV_ID_82543GC_COPPER:
 292        case E1000_DEV_ID_82544EI_FIBER:
 293        default:
 294                wol->supported = 0;
 295                wol->wolopts   = 0;
 296                return;
 297
 298        case E1000_DEV_ID_82546EB_FIBER:
 299                /* Wake events only supported on port A for dual fiber */
 300                if(E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1) {
 301                        wol->supported = 0;
 302                        wol->wolopts   = 0;
 303                        return;
 304                }
 305                /* Fall Through */
 306
 307        case E1000_DEV_ID_82544EI_COPPER:
 308        case E1000_DEV_ID_82544GC_COPPER:
 309        case E1000_DEV_ID_82544GC_LOM:
 310        case E1000_DEV_ID_82540EM:
 311        case E1000_DEV_ID_82540EM_LOM:
 312        case E1000_DEV_ID_82545EM_COPPER:
 313        case E1000_DEV_ID_82545EM_FIBER:
 314        case E1000_DEV_ID_82546EB_COPPER:
 315                wol->supported = WAKE_PHY | WAKE_UCAST | 
 316                                 WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC;
 317                
 318                wol->wolopts = 0;
 319                if(adapter->wol & E1000_WUFC_LNKC)
 320                        wol->wolopts |= WAKE_PHY;
 321                if(adapter->wol & E1000_WUFC_EX)
 322                        wol->wolopts |= WAKE_UCAST;
 323                if(adapter->wol & E1000_WUFC_MC)
 324                        wol->wolopts |= WAKE_MCAST;
 325                if(adapter->wol & E1000_WUFC_BC)
 326                        wol->wolopts |= WAKE_BCAST;
 327                if(adapter->wol & E1000_WUFC_MAG)
 328                        wol->wolopts |= WAKE_MAGIC;
 329                return;
 330        }
 331}
 332
 333static int
 334e1000_ethtool_swol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol)
 335{
 336        struct e1000_hw *hw = &adapter->hw;
 337
 338        switch(adapter->hw.device_id) {
 339        case E1000_DEV_ID_82542:
 340        case E1000_DEV_ID_82543GC_FIBER:
 341        case E1000_DEV_ID_82543GC_COPPER:
 342        case E1000_DEV_ID_82544EI_FIBER:
 343        default:
 344                return wol->wolopts ? -EOPNOTSUPP : 0;
 345
 346        case E1000_DEV_ID_82546EB_FIBER:
 347                /* Wake events only supported on port A for dual fiber */
 348                if(E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)
 349                        return wol->wolopts ? -EOPNOTSUPP : 0;
 350                /* Fall Through */
 351
 352        case E1000_DEV_ID_82544EI_COPPER:
 353        case E1000_DEV_ID_82544GC_COPPER:
 354        case E1000_DEV_ID_82544GC_LOM:
 355        case E1000_DEV_ID_82540EM:
 356        case E1000_DEV_ID_82540EM_LOM:
 357        case E1000_DEV_ID_82545EM_COPPER:
 358        case E1000_DEV_ID_82545EM_FIBER:
 359        case E1000_DEV_ID_82546EB_COPPER:
 360                if(wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE))
 361                        return -EOPNOTSUPP;
 362
 363                adapter->wol = 0;
 364
 365                if(wol->wolopts & WAKE_PHY)
 366                        adapter->wol |= E1000_WUFC_LNKC;
 367                if(wol->wolopts & WAKE_UCAST)
 368                        adapter->wol |= E1000_WUFC_EX;
 369                if(wol->wolopts & WAKE_MCAST)
 370                        adapter->wol |= E1000_WUFC_MC;
 371                if(wol->wolopts & WAKE_BCAST)
 372                        adapter->wol |= E1000_WUFC_BC;
 373                if(wol->wolopts & WAKE_MAGIC)
 374                        adapter->wol |= E1000_WUFC_MAG;
 375        }
 376
 377        return 0;
 378}
 379
 380
 381/* toggle LED 4 times per second = 2 "blinks" per second */
 382#define E1000_ID_INTERVAL       (HZ/4)
 383
 384/* bit defines for adapter->led_status */
 385#define E1000_LED_ON            0
 386
 387static void
 388e1000_led_blink_callback(unsigned long data)
 389{
 390        struct e1000_adapter *adapter = (struct e1000_adapter *) data;
 391        
 392        if(test_and_change_bit(E1000_LED_ON, &adapter->led_status))
 393                e1000_led_off(&adapter->hw);
 394        else
 395                e1000_led_on(&adapter->hw);
 396
 397        mod_timer(&adapter->blink_timer, jiffies + E1000_ID_INTERVAL);
 398}
 399
 400static int
 401e1000_ethtool_led_blink(struct e1000_adapter *adapter, struct ethtool_value *id)
 402{
 403        if(!adapter->blink_timer.function) {
 404                init_timer(&adapter->blink_timer);
 405                adapter->blink_timer.function = e1000_led_blink_callback;
 406                adapter->blink_timer.data = (unsigned long) adapter;
 407        }
 408
 409        e1000_setup_led(&adapter->hw);
 410        mod_timer(&adapter->blink_timer, jiffies);
 411        
 412        set_current_state(TASK_INTERRUPTIBLE);
 413        if(id->data)
 414                schedule_timeout(id->data * HZ);
 415        else
 416                schedule_timeout(MAX_SCHEDULE_TIMEOUT);
 417        
 418        del_timer_sync(&adapter->blink_timer);
 419        e1000_led_off(&adapter->hw);
 420        clear_bit(E1000_LED_ON, &adapter->led_status);
 421        e1000_cleanup_led(&adapter->hw);
 422
 423        return 0;
 424}
 425
 426int
 427e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr)
 428{
 429        struct e1000_adapter *adapter = netdev->priv;
 430        void *addr = ifr->ifr_data;
 431        uint32_t cmd;
 432
 433        if(get_user(cmd, (uint32_t *) addr))
 434                return -EFAULT;
 435
 436        switch(cmd) {
 437        case ETHTOOL_GSET: {
 438                struct ethtool_cmd ecmd = {ETHTOOL_GSET};
 439                e1000_ethtool_gset(adapter, &ecmd);
 440                if(copy_to_user(addr, &ecmd, sizeof(ecmd)))
 441                        return -EFAULT;
 442                return 0;
 443        }
 444        case ETHTOOL_SSET: {
 445                struct ethtool_cmd ecmd;
 446                if(!capable(CAP_NET_ADMIN))
 447                        return -EPERM;
 448                if(copy_from_user(&ecmd, addr, sizeof(ecmd)))
 449                        return -EFAULT;
 450                return e1000_ethtool_sset(adapter, &ecmd);
 451        }
 452        case ETHTOOL_GDRVINFO: {
 453                struct ethtool_drvinfo drvinfo = {ETHTOOL_GDRVINFO};
 454                e1000_ethtool_gdrvinfo(adapter, &drvinfo);
 455                if(copy_to_user(addr, &drvinfo, sizeof(drvinfo)))
 456                        return -EFAULT;
 457                return 0;
 458        }
 459        case ETHTOOL_GREGS: {
 460                struct ethtool_regs regs = {ETHTOOL_GREGS};
 461                uint32_t regs_buff[E1000_REGS_LEN];
 462
 463                if(copy_from_user(&regs, addr, sizeof(regs)))
 464                        return -EFAULT;
 465                e1000_ethtool_gregs(adapter, &regs, regs_buff);
 466                if(copy_to_user(addr, &regs, sizeof(regs)))
 467                        return -EFAULT;
 468
 469                addr += offsetof(struct ethtool_regs, data);
 470                if(copy_to_user(addr, regs_buff, regs.len))
 471                        return -EFAULT;
 472
 473                return 0;
 474        }
 475        case ETHTOOL_NWAY_RST: {
 476                if(!capable(CAP_NET_ADMIN))
 477                        return -EPERM;
 478                if(netif_running(netdev)) {
 479                        e1000_down(adapter);
 480                        e1000_up(adapter);
 481                }
 482                return 0;
 483        }
 484        case ETHTOOL_PHYS_ID: {
 485                struct ethtool_value id;
 486                if(copy_from_user(&id, addr, sizeof(id)))
 487                        return -EFAULT;
 488                return e1000_ethtool_led_blink(adapter, &id);
 489        }
 490        case ETHTOOL_GLINK: {
 491                struct ethtool_value link = {ETHTOOL_GLINK};
 492                link.data = netif_carrier_ok(netdev);
 493                if(copy_to_user(addr, &link, sizeof(link)))
 494                        return -EFAULT;
 495                return 0;
 496        }
 497        case ETHTOOL_GWOL: {
 498                struct ethtool_wolinfo wol = {ETHTOOL_GWOL};
 499                e1000_ethtool_gwol(adapter, &wol);
 500                if(copy_to_user(addr, &wol, sizeof(wol)) != 0)
 501                        return -EFAULT;
 502                return 0;
 503        }
 504        case ETHTOOL_SWOL: {
 505                struct ethtool_wolinfo wol;
 506                if(!capable(CAP_NET_ADMIN))
 507                        return -EPERM;
 508                if(copy_from_user(&wol, addr, sizeof(wol)) != 0)
 509                        return -EFAULT;
 510                return e1000_ethtool_swol(adapter, &wol);
 511        }
 512        case ETHTOOL_GEEPROM: {
 513                struct ethtool_eeprom eeprom = {ETHTOOL_GEEPROM};
 514                uint16_t eeprom_buff[256];
 515                void *ptr;
 516                int err;
 517
 518                if(copy_from_user(&eeprom, addr, sizeof(eeprom)))
 519                        return -EFAULT;
 520
 521                if((err = e1000_ethtool_geeprom(adapter, &eeprom, eeprom_buff))<0)
 522                        return err;
 523
 524                if(copy_to_user(addr, &eeprom, sizeof(eeprom)))
 525                        return -EFAULT;
 526
 527                addr += offsetof(struct ethtool_eeprom, data);
 528                ptr = ((void *)eeprom_buff) + (eeprom.offset & 1);
 529
 530                if(copy_to_user(addr, ptr, eeprom.len))
 531                        return -EFAULT;
 532                return 0;
 533        }
 534        case ETHTOOL_SEEPROM: {
 535                struct ethtool_eeprom eeprom;
 536
 537                if(!capable(CAP_NET_ADMIN))
 538                        return -EPERM;
 539
 540                if(copy_from_user(&eeprom, addr, sizeof(eeprom)))
 541                        return -EFAULT;
 542
 543                addr += offsetof(struct ethtool_eeprom, data);
 544                return e1000_ethtool_seeprom(adapter, &eeprom, addr);
 545        }
 546        default:
 547                return -EOPNOTSUPP;
 548        }
 549}
 550
 551
 552
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.