Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  1 /*
  2  * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved.
  3  * Copyright(c) 2006 Chris Snook <csnook@redhat.com>
  4  * Copyright(c) 2006 Jay Cliburn <jcliburn@gmail.com>
  5  *
  6  * Derived from Intel e1000 driver
  7  * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
  8  *
  9  * This program is free software; you can redistribute it and/or modify it
 10  * under the terms of the GNU General Public License as published by the Free
 11  * Software Foundation; either version 2 of the License, or (at your option)
 12  * any later version.
 13  *
 14  * This program is distributed in the hope that it will be useful, but WITHOUT
 15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 16  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 17  * more details.
 18  *
 19  * You should have received a copy of the GNU General Public License along with
 20  * this program; if not, write to the Free Software Foundation, Inc., 59
 21  * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 22  */
 23 
 24 #include <linux/types.h>
 25 #include <linux/pci.h>
 26 #include <linux/ethtool.h>
 27 #include <linux/netdevice.h>
 28 #include <linux/mii.h>
 29 #include <asm/uaccess.h>
 30 
 31 #include "atl1.h"
 32 
 33 struct atl1_stats {
 34         char stat_string[ETH_GSTRING_LEN];
 35         int sizeof_stat;
 36         int stat_offset;
 37 };
 38 
 39 #define ATL1_STAT(m) sizeof(((struct atl1_adapter *)0)->m), \
 40         offsetof(struct atl1_adapter, m)
 41 
 42 static struct atl1_stats atl1_gstrings_stats[] = {
 43         {"rx_packets", ATL1_STAT(soft_stats.rx_packets)},
 44         {"tx_packets", ATL1_STAT(soft_stats.tx_packets)},
 45         {"rx_bytes", ATL1_STAT(soft_stats.rx_bytes)},
 46         {"tx_bytes", ATL1_STAT(soft_stats.tx_bytes)},
 47         {"rx_errors", ATL1_STAT(soft_stats.rx_errors)},
 48         {"tx_errors", ATL1_STAT(soft_stats.tx_errors)},
 49         {"rx_dropped", ATL1_STAT(net_stats.rx_dropped)},
 50         {"tx_dropped", ATL1_STAT(net_stats.tx_dropped)},
 51         {"multicast", ATL1_STAT(soft_stats.multicast)},
 52         {"collisions", ATL1_STAT(soft_stats.collisions)},
 53         {"rx_length_errors", ATL1_STAT(soft_stats.rx_length_errors)},
 54         {"rx_over_errors", ATL1_STAT(soft_stats.rx_missed_errors)},
 55         {"rx_crc_errors", ATL1_STAT(soft_stats.rx_crc_errors)},
 56         {"rx_frame_errors", ATL1_STAT(soft_stats.rx_frame_errors)},
 57         {"rx_fifo_errors", ATL1_STAT(soft_stats.rx_fifo_errors)},
 58         {"rx_missed_errors", ATL1_STAT(soft_stats.rx_missed_errors)},
 59         {"tx_aborted_errors", ATL1_STAT(soft_stats.tx_aborted_errors)},
 60         {"tx_carrier_errors", ATL1_STAT(soft_stats.tx_carrier_errors)},
 61         {"tx_fifo_errors", ATL1_STAT(soft_stats.tx_fifo_errors)},
 62         {"tx_window_errors", ATL1_STAT(soft_stats.tx_window_errors)},
 63         {"tx_abort_exce_coll", ATL1_STAT(soft_stats.excecol)},
 64         {"tx_abort_late_coll", ATL1_STAT(soft_stats.latecol)},
 65         {"tx_deferred_ok", ATL1_STAT(soft_stats.deffer)},
 66         {"tx_single_coll_ok", ATL1_STAT(soft_stats.scc)},
 67         {"tx_multi_coll_ok", ATL1_STAT(soft_stats.mcc)},
 68         {"tx_underun", ATL1_STAT(soft_stats.tx_underun)},
 69         {"tx_trunc", ATL1_STAT(soft_stats.tx_trunc)},
 70         {"tx_pause", ATL1_STAT(soft_stats.tx_pause)},
 71         {"rx_pause", ATL1_STAT(soft_stats.rx_pause)},
 72         {"rx_rrd_ov", ATL1_STAT(soft_stats.rx_rrd_ov)},
 73         {"rx_trunc", ATL1_STAT(soft_stats.rx_trunc)}
 74 };
 75 
 76 static void atl1_get_ethtool_stats(struct net_device *netdev,
 77                                 struct ethtool_stats *stats, u64 *data)
 78 {
 79         struct atl1_adapter *adapter = netdev_priv(netdev);
 80         int i;
 81         char *p;
 82 
 83         for (i = 0; i < ARRAY_SIZE(atl1_gstrings_stats); i++) {
 84                 p = (char *)adapter+atl1_gstrings_stats[i].stat_offset;
 85                 data[i] = (atl1_gstrings_stats[i].sizeof_stat ==
 86                         sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
 87         }
 88 
 89 }
 90 
 91 static int atl1_get_sset_count(struct net_device *netdev, int sset)
 92 {
 93         switch (sset) {
 94         case ETH_SS_STATS:
 95                 return ARRAY_SIZE(atl1_gstrings_stats);
 96         default:
 97                 return -EOPNOTSUPP;
 98         }
 99 }
100 
101 static int atl1_get_settings(struct net_device *netdev,
102                                 struct ethtool_cmd *ecmd)
103 {
104         struct atl1_adapter *adapter = netdev_priv(netdev);
105         struct atl1_hw *hw = &adapter->hw;
106 
107         ecmd->supported = (SUPPORTED_10baseT_Half |
108                            SUPPORTED_10baseT_Full |
109                            SUPPORTED_100baseT_Half |
110                            SUPPORTED_100baseT_Full |
111                            SUPPORTED_1000baseT_Full |
112                            SUPPORTED_Autoneg | SUPPORTED_TP);
113         ecmd->advertising = ADVERTISED_TP;
114         if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
115             hw->media_type == MEDIA_TYPE_1000M_FULL) {
116                 ecmd->advertising |= ADVERTISED_Autoneg;
117                 if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR) {
118                         ecmd->advertising |= ADVERTISED_Autoneg;
119                         ecmd->advertising |=
120                             (ADVERTISED_10baseT_Half |
121                              ADVERTISED_10baseT_Full |
122                              ADVERTISED_100baseT_Half |
123                              ADVERTISED_100baseT_Full |
124                              ADVERTISED_1000baseT_Full);
125                 }
126                 else
127                         ecmd->advertising |= (ADVERTISED_1000baseT_Full);
128         }
129         ecmd->port = PORT_TP;
130         ecmd->phy_address = 0;
131         ecmd->transceiver = XCVR_INTERNAL;
132 
133         if (netif_carrier_ok(adapter->netdev)) {
134                 u16 link_speed, link_duplex;
135                 atl1_get_speed_and_duplex(hw, &link_speed, &link_duplex);
136                 ecmd->speed = link_speed;
137                 if (link_duplex == FULL_DUPLEX)
138                         ecmd->duplex = DUPLEX_FULL;
139                 else
140                         ecmd->duplex = DUPLEX_HALF;
141         } else {
142                 ecmd->speed = -1;
143                 ecmd->duplex = -1;
144         }
145         if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
146             hw->media_type == MEDIA_TYPE_1000M_FULL)
147                 ecmd->autoneg = AUTONEG_ENABLE;
148         else
149                 ecmd->autoneg = AUTONEG_DISABLE;
150 
151         return 0;
152 }
153 
154 static int atl1_set_settings(struct net_device *netdev,
155                                 struct ethtool_cmd *ecmd)
156 {
157         struct atl1_adapter *adapter = netdev_priv(netdev);
158         struct atl1_hw *hw = &adapter->hw;
159         u16 phy_data;
160         int ret_val = 0;
161         u16 old_media_type = hw->media_type;
162 
163         if (netif_running(adapter->netdev)) {
164                 dev_dbg(&adapter->pdev->dev, "ethtool shutting down adapter\n");
165                 atl1_down(adapter);
166         }
167 
168         if (ecmd->autoneg == AUTONEG_ENABLE)
169                 hw->media_type = MEDIA_TYPE_AUTO_SENSOR;
170         else {
171                 if (ecmd->speed == SPEED_1000) {
172                         if (ecmd->duplex != DUPLEX_FULL) {
173                                 dev_warn(&adapter->pdev->dev,
174                                         "can't force to 1000M half duplex\n");
175                                 ret_val = -EINVAL;
176                                 goto exit_sset;
177                         }
178                         hw->media_type = MEDIA_TYPE_1000M_FULL;
179                 } else if (ecmd->speed == SPEED_100) {
180                         if (ecmd->duplex == DUPLEX_FULL) {
181                                 hw->media_type = MEDIA_TYPE_100M_FULL;
182                         } else
183                                 hw->media_type = MEDIA_TYPE_100M_HALF;
184                 } else {
185                         if (ecmd->duplex == DUPLEX_FULL)
186                                 hw->media_type = MEDIA_TYPE_10M_FULL;
187                         else
188                                 hw->media_type = MEDIA_TYPE_10M_HALF;
189                 }
190         }
191         switch (hw->media_type) {
192         case MEDIA_TYPE_AUTO_SENSOR:
193                 ecmd->advertising =
194                     ADVERTISED_10baseT_Half |
195                     ADVERTISED_10baseT_Full |
196                     ADVERTISED_100baseT_Half |
197                     ADVERTISED_100baseT_Full |
198                     ADVERTISED_1000baseT_Full |
199                     ADVERTISED_Autoneg | ADVERTISED_TP;
200                 break;
201         case MEDIA_TYPE_1000M_FULL:
202                 ecmd->advertising =
203                     ADVERTISED_1000baseT_Full |
204                     ADVERTISED_Autoneg | ADVERTISED_TP;
205                 break;
206         default:
207                 ecmd->advertising = 0;
208                 break;
209         }
210         if (atl1_phy_setup_autoneg_adv(hw)) {
211                 ret_val = -EINVAL;
212                 dev_warn(&adapter->pdev->dev,
213                         "invalid ethtool speed/duplex setting\n");
214                 goto exit_sset;
215         }
216         if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
217             hw->media_type == MEDIA_TYPE_1000M_FULL)
218                 phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN;
219         else {
220                 switch (hw->media_type) {
221                 case MEDIA_TYPE_100M_FULL:
222                         phy_data =
223                             MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 |
224                             MII_CR_RESET;
225                         break;
226                 case MEDIA_TYPE_100M_HALF:
227                         phy_data = MII_CR_SPEED_100 | MII_CR_RESET;
228                         break;
229                 case MEDIA_TYPE_10M_FULL:
230                         phy_data =
231                             MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET;
232                         break;
233                 default:        /* MEDIA_TYPE_10M_HALF: */
234                         phy_data = MII_CR_SPEED_10 | MII_CR_RESET;
235                         break;
236                 }
237         }
238         atl1_write_phy_reg(hw, MII_BMCR, phy_data);
239 exit_sset:
240         if (ret_val)
241                 hw->media_type = old_media_type;
242 
243         if (netif_running(adapter->netdev)) {
244                 dev_dbg(&adapter->pdev->dev, "ethtool starting adapter\n");
245                 atl1_up(adapter);
246         } else if (!ret_val) {
247                 dev_dbg(&adapter->pdev->dev, "ethtool resetting adapter\n");
248                 atl1_reset(adapter);
249         }
250         return ret_val;
251 }
252 
253 static void atl1_get_drvinfo(struct net_device *netdev,
254                                 struct ethtool_drvinfo *drvinfo)
255 {
256         struct atl1_adapter *adapter = netdev_priv(netdev);
257 
258         strncpy(drvinfo->driver, atl1_driver_name, sizeof(drvinfo->driver));
259         strncpy(drvinfo->version, atl1_driver_version,
260                 sizeof(drvinfo->version));
261         strncpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
262         strncpy(drvinfo->bus_info, pci_name(adapter->pdev),
263                 sizeof(drvinfo->bus_info));
264         drvinfo->eedump_len = ATL1_EEDUMP_LEN;
265 }
266 
267 static void atl1_get_wol(struct net_device *netdev,
268                             struct ethtool_wolinfo *wol)
269 {
270         struct atl1_adapter *adapter = netdev_priv(netdev);
271 
272         wol->supported = WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC;
273         wol->wolopts = 0;
274         if (adapter->wol & ATL1_WUFC_EX)
275                 wol->wolopts |= WAKE_UCAST;
276         if (adapter->wol & ATL1_WUFC_MC)
277                 wol->wolopts |= WAKE_MCAST;
278         if (adapter->wol & ATL1_WUFC_BC)
279                 wol->wolopts |= WAKE_BCAST;
280         if (adapter->wol & ATL1_WUFC_MAG)
281                 wol->wolopts |= WAKE_MAGIC;
282         return;
283 }
284 
285 static int atl1_set_wol(struct net_device *netdev,
286                         struct ethtool_wolinfo *wol)
287 {
288         struct atl1_adapter *adapter = netdev_priv(netdev);
289 
290         if (wol->wolopts & (WAKE_PHY | WAKE_ARP | WAKE_MAGICSECURE))
291                 return -EOPNOTSUPP;
292         adapter->wol = 0;
293         if (wol->wolopts & WAKE_UCAST)
294                 adapter->wol |= ATL1_WUFC_EX;
295         if (wol->wolopts & WAKE_MCAST)
296                 adapter->wol |= ATL1_WUFC_MC;
297         if (wol->wolopts & WAKE_BCAST)
298                 adapter->wol |= ATL1_WUFC_BC;
299         if (wol->wolopts & WAKE_MAGIC)
300                 adapter->wol |= ATL1_WUFC_MAG;
301         return 0;
302 }
303 
304 static void atl1_get_ringparam(struct net_device *netdev,
305                             struct ethtool_ringparam *ring)
306 {
307         struct atl1_adapter *adapter = netdev_priv(netdev);
308         struct atl1_tpd_ring *txdr = &adapter->tpd_ring;
309         struct atl1_rfd_ring *rxdr = &adapter->rfd_ring;
310 
311         ring->rx_max_pending = ATL1_MAX_RFD;
312         ring->tx_max_pending = ATL1_MAX_TPD;
313         ring->rx_mini_max_pending = 0;
314         ring->rx_jumbo_max_pending = 0;
315         ring->rx_pending = rxdr->count;
316         ring->tx_pending = txdr->count;
317         ring->rx_mini_pending = 0;
318         ring->rx_jumbo_pending = 0;
319 }
320 
321 static int atl1_set_ringparam(struct net_device *netdev,
322                                 struct ethtool_ringparam *ring)
323 {
324         struct atl1_adapter *adapter = netdev_priv(netdev);
325         struct atl1_tpd_ring *tpdr = &adapter->tpd_ring;
326         struct atl1_rrd_ring *rrdr = &adapter->rrd_ring;
327         struct atl1_rfd_ring *rfdr = &adapter->rfd_ring;
328 
329         struct atl1_tpd_ring tpd_old, tpd_new;
330         struct atl1_rfd_ring rfd_old, rfd_new;
331         struct atl1_rrd_ring rrd_old, rrd_new;
332         struct atl1_ring_header rhdr_old, rhdr_new;
333         int err;
334 
335         tpd_old = adapter->tpd_ring;
336         rfd_old = adapter->rfd_ring;
337         rrd_old = adapter->rrd_ring;
338         rhdr_old = adapter->ring_header;
339 
340         if (netif_running(adapter->netdev))
341                 atl1_down(adapter);
342 
343         rfdr->count = (u16) max(ring->rx_pending, (u32) ATL1_MIN_RFD);
344         rfdr->count = rfdr->count > ATL1_MAX_RFD ? ATL1_MAX_RFD :
345                         rfdr->count;
346         rfdr->count = (rfdr->count + 3) & ~3;
347         rrdr->count = rfdr->count;
348 
349         tpdr->count = (u16) max(ring->tx_pending, (u32) ATL1_MIN_TPD);
350         tpdr->count = tpdr->count > ATL1_MAX_TPD ? ATL1_MAX_TPD :
351                         tpdr->count;
352         tpdr->count = (tpdr->count + 3) & ~3;
353 
354         if (netif_running(adapter->netdev)) {
355                 /* try to get new resources before deleting old */
356                 err = atl1_setup_ring_resources(adapter);
357                 if (err)
358                         goto err_setup_ring;
359 
360                 /*
361                  * save the new, restore the old in order to free it,
362                  * then restore the new back again
363                  */
364 
365                 rfd_new = adapter->rfd_ring;
366                 rrd_new = adapter->rrd_ring;
367                 tpd_new = adapter->tpd_ring;
368                 rhdr_new = adapter->ring_header;
369                 adapter->rfd_ring = rfd_old;
370                 adapter->rrd_ring = rrd_old;
371                 adapter->tpd_ring = tpd_old;
372                 adapter->ring_header = rhdr_old;
373                 atl1_free_ring_resources(adapter);
374                 adapter->rfd_ring = rfd_new;
375                 adapter->rrd_ring = rrd_new;
376                 adapter->tpd_ring = tpd_new;
377                 adapter->ring_header = rhdr_new;
378 
379                 err = atl1_up(adapter);
380                 if (err)
381                         return err;
382         }
383         return 0;
384 
385 err_setup_ring:
386         adapter->rfd_ring = rfd_old;
387         adapter->rrd_ring = rrd_old;
388         adapter->tpd_ring = tpd_old;
389         adapter->ring_header = rhdr_old;
390         atl1_up(adapter);
391         return err;
392 }
393 
394 static void atl1_get_pauseparam(struct net_device *netdev,
395                              struct ethtool_pauseparam *epause)
396 {
397         struct atl1_adapter *adapter = netdev_priv(netdev);
398         struct atl1_hw *hw = &adapter->hw;
399 
400         if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
401             hw->media_type == MEDIA_TYPE_1000M_FULL) {
402                 epause->autoneg = AUTONEG_ENABLE;
403         } else {
404                 epause->autoneg = AUTONEG_DISABLE;
405         }
406         epause->rx_pause = 1;
407         epause->tx_pause = 1;
408 }
409 
410 static int atl1_set_pauseparam(struct net_device *netdev,
411                              struct ethtool_pauseparam *epause)
412 {
413         struct atl1_adapter *adapter = netdev_priv(netdev);
414         struct atl1_hw *hw = &adapter->hw;
415 
416         if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
417             hw->media_type == MEDIA_TYPE_1000M_FULL) {
418                 epause->autoneg = AUTONEG_ENABLE;
419         } else {
420                 epause->autoneg = AUTONEG_DISABLE;
421         }
422 
423         epause->rx_pause = 1;
424         epause->tx_pause = 1;
425 
426         return 0;
427 }
428 
429 static u32 atl1_get_rx_csum(struct net_device *netdev)
430 {
431         return 1;
432 }
433 
434 static void atl1_get_strings(struct net_device *netdev, u32 stringset,
435                                 u8 *data)
436 {
437         u8 *p = data;
438         int i;
439 
440         switch (stringset) {
441         case ETH_SS_STATS:
442                 for (i = 0; i < ARRAY_SIZE(atl1_gstrings_stats); i++) {
443                         memcpy(p, atl1_gstrings_stats[i].stat_string,
444                                 ETH_GSTRING_LEN);
445                         p += ETH_GSTRING_LEN;
446                 }
447                 break;
448         }
449 }
450 
451 static int atl1_nway_reset(struct net_device *netdev)
452 {
453         struct atl1_adapter *adapter = netdev_priv(netdev);
454         struct atl1_hw *hw = &adapter->hw;
455 
456         if (netif_running(netdev)) {
457                 u16 phy_data;
458                 atl1_down(adapter);
459 
460                 if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
461                         hw->media_type == MEDIA_TYPE_1000M_FULL) {
462                         phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN;
463                 } else {
464                         switch (hw->media_type) {
465                         case MEDIA_TYPE_100M_FULL:
466                                 phy_data = MII_CR_FULL_DUPLEX |
467                                         MII_CR_SPEED_100 | MII_CR_RESET;
468                                 break;
469                         case MEDIA_TYPE_100M_HALF:
470                                 phy_data = MII_CR_SPEED_100 | MII_CR_RESET;
471                                 break;
472                         case MEDIA_TYPE_10M_FULL:
473                                 phy_data = MII_CR_FULL_DUPLEX |
474                                         MII_CR_SPEED_10 | MII_CR_RESET;
475                                 break;
476                         default:  /* MEDIA_TYPE_10M_HALF */
477                                 phy_data = MII_CR_SPEED_10 | MII_CR_RESET;
478                         }
479                 }
480                 atl1_write_phy_reg(hw, MII_BMCR, phy_data);
481                 atl1_up(adapter);
482         }
483         return 0;
484 }
485 
486 const struct ethtool_ops atl1_ethtool_ops = {
487         .get_settings           = atl1_get_settings,
488         .set_settings           = atl1_set_settings,
489         .get_drvinfo            = atl1_get_drvinfo,
490         .get_wol                = atl1_get_wol,
491         .set_wol                = atl1_set_wol,
492         .get_ringparam          = atl1_get_ringparam,
493         .set_ringparam          = atl1_set_ringparam,
494         .get_pauseparam         = atl1_get_pauseparam,
495         .set_pauseparam         = atl1_set_pauseparam,
496         .get_rx_csum            = atl1_get_rx_csum,
497         .set_tx_csum            = ethtool_op_set_tx_hw_csum,
498         .get_link               = ethtool_op_get_link,
499         .set_sg                 = ethtool_op_set_sg,
500         .get_strings            = atl1_get_strings,
501         .nway_reset             = atl1_nway_reset,
502         .get_ethtool_stats      = atl1_get_ethtool_stats,
503         .get_sset_count         = atl1_get_sset_count,
504         .set_tso                = ethtool_op_set_tso,
505 };
506 
  This page was automatically generated by the LXR engine.