2005-08-31 20:53:10 +04:00
/*
* ASIX AX8817X based USB 2.0 Ethernet Devices
2006-07-29 18:12:50 +04:00
* Copyright ( C ) 2003 - 2006 David Hollis < dhollis @ davehollis . com >
2005-08-31 20:53:10 +04:00
* Copyright ( C ) 2005 Phil Chang < pchang23 @ sbcglobal . net >
2006-07-29 18:12:50 +04:00
* Copyright ( C ) 2006 James Painter < jamie . painter @ iname . com >
2005-08-31 20:53:10 +04:00
* Copyright ( c ) 2002 - 2003 TiVo Inc .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* 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
2013-12-06 18:28:46 +04:00
* along with this program ; if not , see < http : //www.gnu.org/licenses/>.
2005-08-31 20:53:10 +04:00
*/
2012-07-13 09:26:30 +04:00
# include "asix.h"
int asix_read_cmd ( struct usbnet * dev , u8 cmd , u16 value , u16 index ,
u16 size , void * data )
2005-08-31 20:53:10 +04:00
{
2012-10-24 23:46:55 +04:00
int ret ;
ret = usbnet_read_cmd ( dev , cmd ,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
value , index , data , size ) ;
if ( ret ! = size & & ret > = 0 )
return - EINVAL ;
return ret ;
2005-08-31 20:53:10 +04:00
}
2012-07-13 09:26:30 +04:00
int asix_write_cmd ( struct usbnet * dev , u8 cmd , u16 value , u16 index ,
u16 size , void * data )
2005-08-31 20:53:10 +04:00
{
2012-10-24 23:46:55 +04:00
return usbnet_write_cmd ( dev , cmd ,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
value , index , data , size ) ;
2005-08-31 20:53:10 +04:00
}
2012-07-13 09:26:30 +04:00
void asix_write_cmd_async ( struct usbnet * dev , u8 cmd , u16 value , u16 index ,
u16 size , void * data )
2006-07-29 18:12:50 +04:00
{
2012-10-24 23:46:55 +04:00
usbnet_write_cmd_async ( dev , cmd ,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
value , index , data , size ) ;
2006-07-29 18:12:50 +04:00
}
2013-01-16 08:24:07 +04:00
int asix_rx_fixup_internal ( struct usbnet * dev , struct sk_buff * skb ,
struct asix_rx_fixup_info * rx )
2006-07-29 18:12:50 +04:00
{
2012-03-15 00:18:32 +04:00
int offset = 0 ;
2015-10-02 16:29:04 +03:00
u16 size ;
2006-07-29 18:12:50 +04:00
2015-10-02 16:29:07 +03:00
/* When an Ethernet frame spans multiple URB socket buffers,
* do a sanity test for the Data header synchronisation .
* Attempt to detect the situation of the previous socket buffer having
* been truncated or a socket buffer was missing . These situations
* cause a discontinuity in the data stream and therefore need to avoid
* appending bad data to the end of the current netdev socket buffer .
* Also avoid unnecessarily discarding a good current netdev socket
* buffer .
*/
if ( rx - > remaining & & ( rx - > remaining + sizeof ( u32 ) < = skb - > len ) ) {
offset = ( ( rx - > remaining + 1 ) & 0xfffe ) + sizeof ( u32 ) ;
rx - > header = get_unaligned_le32 ( skb - > data + offset ) ;
offset = 0 ;
size = ( u16 ) ( rx - > header & 0x7ff ) ;
if ( size ! = ( ( ~ rx - > header > > 16 ) & 0x7ff ) ) {
netdev_err ( dev - > net , " asix_rx_fixup() Data Header synchronisation was lost, remaining %d \n " ,
rx - > remaining ) ;
2015-10-02 16:29:08 +03:00
if ( rx - > ax_skb ) {
kfree_skb ( rx - > ax_skb ) ;
rx - > ax_skb = NULL ;
/* Discard the incomplete netdev Ethernet frame
* and assume the Data header is at the start of
* the current URB socket buffer .
*/
}
2015-10-02 16:29:07 +03:00
rx - > remaining = 0 ;
}
}
2013-01-16 08:24:07 +04:00
while ( offset + sizeof ( u16 ) < = skb - > len ) {
2015-10-02 16:29:04 +03:00
u16 copy_length ;
2013-01-16 08:24:07 +04:00
unsigned char * data ;
2006-07-29 18:12:50 +04:00
2015-10-02 16:29:04 +03:00
if ( ! rx - > remaining ) {
2015-10-02 16:29:05 +03:00
if ( skb - > len - offset = = sizeof ( u16 ) ) {
rx - > header = get_unaligned_le16 (
skb - > data + offset ) ;
rx - > split_head = true ;
offset + = sizeof ( u16 ) ;
break ;
}
if ( rx - > split_head = = true ) {
rx - > header | = ( get_unaligned_le16 (
skb - > data + offset ) < < 16 ) ;
rx - > split_head = false ;
offset + = sizeof ( u16 ) ;
2013-01-16 08:24:07 +04:00
} else {
rx - > header = get_unaligned_le32 ( skb - > data +
offset ) ;
offset + = sizeof ( u32 ) ;
}
2011-07-26 20:44:46 +04:00
2015-10-02 16:29:04 +03:00
/* take frame length from Data header 32-bit word */
size = ( u16 ) ( rx - > header & 0x7ff ) ;
if ( size ! = ( ( ~ rx - > header > > 16 ) & 0x7ff ) ) {
2013-01-16 08:24:07 +04:00
netdev_err ( dev - > net , " asix_rx_fixup() Bad Header Length 0x%x, offset %d \n " ,
rx - > header , offset ) ;
return 0 ;
}
2015-10-02 16:29:06 +03:00
if ( size > dev - > net - > mtu + ETH_HLEN + VLAN_HLEN ) {
netdev_err ( dev - > net , " asix_rx_fixup() Bad RX Length %d \n " ,
size ) ;
return 0 ;
}
2015-10-02 16:29:08 +03:00
/* Sometimes may fail to get a netdev socket buffer but
* continue to process the URB socket buffer so that
* synchronisation of the Ethernet frame Data header
* word is maintained .
*/
2015-10-02 16:29:04 +03:00
rx - > ax_skb = netdev_alloc_skb_ip_align ( dev - > net , size ) ;
2010-05-18 04:18:28 +04:00
2015-10-02 16:29:06 +03:00
rx - > remaining = size ;
2006-07-29 18:12:50 +04:00
}
2015-10-02 16:29:04 +03:00
if ( rx - > remaining > skb - > len - offset ) {
copy_length = skb - > len - offset ;
rx - > remaining - = copy_length ;
} else {
copy_length = rx - > remaining ;
rx - > remaining = 0 ;
2013-01-16 08:24:07 +04:00
}
2006-07-29 18:12:50 +04:00
2015-10-02 16:29:08 +03:00
if ( rx - > ax_skb ) {
data = skb_put ( rx - > ax_skb , copy_length ) ;
memcpy ( data , skb - > data + offset , copy_length ) ;
if ( ! rx - > remaining )
usbnet_skb_return ( dev , rx - > ax_skb ) ;
}
2013-01-16 08:24:07 +04:00
2015-10-02 16:29:04 +03:00
offset + = ( copy_length + 1 ) & 0xfffe ;
2006-07-29 18:12:50 +04:00
}
2012-03-15 00:18:32 +04:00
if ( skb - > len ! = offset ) {
2013-01-16 08:24:07 +04:00
netdev_err ( dev - > net , " asix_rx_fixup() Bad SKB Length %d, %d \n " ,
skb - > len , offset ) ;
2006-07-29 18:12:50 +04:00
return 0 ;
}
2013-01-16 08:24:07 +04:00
2006-07-29 18:12:50 +04:00
return 1 ;
}
2013-01-16 08:24:07 +04:00
int asix_rx_fixup_common ( struct usbnet * dev , struct sk_buff * skb )
{
struct asix_common_private * dp = dev - > driver_priv ;
struct asix_rx_fixup_info * rx = & dp - > rx_fixup_info ;
return asix_rx_fixup_internal ( dev , skb , rx ) ;
}
2012-07-13 09:26:30 +04:00
struct sk_buff * asix_tx_fixup ( struct usbnet * dev , struct sk_buff * skb ,
gfp_t flags )
2006-07-29 18:12:50 +04:00
{
int padlen ;
int headroom = skb_headroom ( skb ) ;
int tailroom = skb_tailroom ( skb ) ;
u32 packet_len ;
u32 padbytes = 0xffff0000 ;
2012-04-24 02:05:38 +04:00
padlen = ( ( skb - > len + 4 ) & ( dev - > maxpacket - 1 ) ) ? 0 : 4 ;
2006-07-29 18:12:50 +04:00
2012-07-05 08:31:01 +04:00
/* We need to push 4 bytes in front of frame (packet_len)
* and maybe add 4 bytes after the end ( if padlen is 4 )
*
* Avoid skb_copy_expand ( ) expensive call , using following rules :
* - We are allowed to push 4 bytes in headroom if skb_header_cloned ( )
* is false ( and if we have 4 bytes of headroom )
* - We are allowed to put 4 bytes at tail if skb_cloned ( )
* is false ( and if we have 4 bytes of tailroom )
*
* TCP packets for example are cloned , but skb_header_release ( )
* was called in tcp stack , allowing us to use headroom for our needs .
*/
if ( ! skb_header_cloned ( skb ) & &
! ( padlen & & skb_cloned ( skb ) ) & &
headroom + tailroom > = 4 + padlen ) {
/* following should not happen, but better be safe */
if ( headroom < 4 | |
tailroom < padlen ) {
2006-07-29 18:12:50 +04:00
skb - > data = memmove ( skb - > head + 4 , skb - > data , skb - > len ) ;
2007-04-20 07:29:13 +04:00
skb_set_tail_pointer ( skb , skb - > len ) ;
2006-07-29 18:12:50 +04:00
}
} else {
struct sk_buff * skb2 ;
2012-07-05 08:31:01 +04:00
2006-07-29 18:12:50 +04:00
skb2 = skb_copy_expand ( skb , 4 , padlen , flags ) ;
dev_kfree_skb_any ( skb ) ;
skb = skb2 ;
if ( ! skb )
return NULL ;
}
2012-07-05 08:31:01 +04:00
packet_len = ( ( skb - > len ^ 0x0000ffff ) < < 16 ) + skb - > len ;
2006-07-29 18:12:50 +04:00
skb_push ( skb , 4 ) ;
2007-02-05 20:03:03 +03:00
cpu_to_le32s ( & packet_len ) ;
2007-03-31 18:55:19 +04:00
skb_copy_to_linear_data ( skb , & packet_len , sizeof ( packet_len ) ) ;
2006-07-29 18:12:50 +04:00
2012-04-24 02:05:38 +04:00
if ( padlen ) {
2007-02-05 20:03:03 +03:00
cpu_to_le32s ( & padbytes ) ;
2007-04-20 07:29:13 +04:00
memcpy ( skb_tail_pointer ( skb ) , & padbytes , sizeof ( padbytes ) ) ;
2006-07-29 18:12:50 +04:00
skb_put ( skb , sizeof ( padbytes ) ) ;
}
2015-02-26 22:34:37 +03:00
2015-03-25 23:41:33 +03:00
usbnet_set_skb_tx_stats ( skb , 1 , 0 ) ;
2006-07-29 18:12:50 +04:00
return skb ;
}
2012-07-13 09:26:30 +04:00
int asix_set_sw_mii ( struct usbnet * dev )
2006-03-29 05:15:42 +04:00
{
int ret ;
ret = asix_write_cmd ( dev , AX_CMD_SET_SW_MII , 0x0000 , 0 , 0 , NULL ) ;
if ( ret < 0 )
2010-02-17 13:30:23 +03:00
netdev_err ( dev - > net , " Failed to enable software MII access \n " ) ;
2006-03-29 05:15:42 +04:00
return ret ;
}
2012-07-13 09:26:30 +04:00
int asix_set_hw_mii ( struct usbnet * dev )
2006-03-29 05:15:42 +04:00
{
int ret ;
ret = asix_write_cmd ( dev , AX_CMD_SET_HW_MII , 0x0000 , 0 , 0 , NULL ) ;
if ( ret < 0 )
2010-02-17 13:30:23 +03:00
netdev_err ( dev - > net , " Failed to enable hardware MII access \n " ) ;
2006-03-29 05:15:42 +04:00
return ret ;
}
2012-07-13 09:26:31 +04:00
int asix_read_phy_addr ( struct usbnet * dev , int internal )
2006-03-29 05:15:42 +04:00
{
2012-07-13 09:26:31 +04:00
int offset = ( internal ? 1 : 0 ) ;
2007-12-22 20:42:36 +03:00
u8 buf [ 2 ] ;
int ret = asix_read_cmd ( dev , AX_CMD_READ_PHY_ID , 0 , 0 , 2 , buf ) ;
2006-03-29 05:15:42 +04:00
2010-02-17 13:30:23 +03:00
netdev_dbg ( dev - > net , " asix_get_phy_addr() \n " ) ;
2006-07-29 18:12:50 +04:00
2007-12-22 20:42:36 +03:00
if ( ret < 0 ) {
2010-02-17 13:30:23 +03:00
netdev_err ( dev - > net , " Error reading PHYID register: %02x \n " , ret ) ;
2007-12-22 20:42:36 +03:00
goto out ;
2006-03-29 05:15:42 +04:00
}
2010-02-17 13:30:23 +03:00
netdev_dbg ( dev - > net , " asix_get_phy_addr() returning 0x%04x \n " ,
* ( ( __le16 * ) buf ) ) ;
2012-07-13 09:26:31 +04:00
ret = buf [ offset ] ;
2007-12-22 20:42:36 +03:00
out :
2006-03-29 05:15:42 +04:00
return ret ;
}
2012-07-13 09:26:31 +04:00
int asix_get_phy_addr ( struct usbnet * dev )
{
/* return the address of the internal phy */
return asix_read_phy_addr ( dev , 1 ) ;
}
2012-07-13 09:26:30 +04:00
int asix_sw_reset ( struct usbnet * dev , u8 flags )
2006-03-29 05:15:42 +04:00
{
int ret ;
ret = asix_write_cmd ( dev , AX_CMD_SW_RESET , flags , 0 , 0 , NULL ) ;
if ( ret < 0 )
2010-02-17 13:30:23 +03:00
netdev_err ( dev - > net , " Failed to send software reset: %02x \n " , ret ) ;
2006-07-29 18:12:50 +04:00
return ret ;
}
2006-03-29 05:15:42 +04:00
2012-07-13 09:26:30 +04:00
u16 asix_read_rx_ctl ( struct usbnet * dev )
2006-07-29 18:12:50 +04:00
{
2007-12-22 20:42:36 +03:00
__le16 v ;
int ret = asix_read_cmd ( dev , AX_CMD_READ_RX_CTL , 0 , 0 , 2 , & v ) ;
2006-07-29 18:12:50 +04:00
2007-12-22 20:42:36 +03:00
if ( ret < 0 ) {
2010-02-17 13:30:23 +03:00
netdev_err ( dev - > net , " Error reading RX_CTL register: %02x \n " , ret ) ;
2007-12-22 20:42:36 +03:00
goto out ;
2006-07-29 18:12:50 +04:00
}
2007-12-22 20:42:36 +03:00
ret = le16_to_cpu ( v ) ;
out :
2006-03-29 05:15:42 +04:00
return ret ;
}
2012-07-13 09:26:30 +04:00
int asix_write_rx_ctl ( struct usbnet * dev , u16 mode )
2006-03-29 05:15:42 +04:00
{
int ret ;
2010-02-17 13:30:23 +03:00
netdev_dbg ( dev - > net , " asix_write_rx_ctl() - mode = 0x%04x \n " , mode ) ;
2006-03-29 05:15:42 +04:00
ret = asix_write_cmd ( dev , AX_CMD_WRITE_RX_CTL , mode , 0 , 0 , NULL ) ;
if ( ret < 0 )
2010-02-17 13:30:23 +03:00
netdev_err ( dev - > net , " Failed to write RX_CTL mode to 0x%04x: %02x \n " ,
mode , ret ) ;
2006-03-29 05:15:42 +04:00
return ret ;
}
2012-07-13 09:26:30 +04:00
u16 asix_read_medium_status ( struct usbnet * dev )
2005-08-31 20:53:10 +04:00
{
2007-12-22 20:42:36 +03:00
__le16 v ;
int ret = asix_read_cmd ( dev , AX_CMD_READ_MEDIUM_STATUS , 0 , 0 , 2 , & v ) ;
2005-08-31 20:53:10 +04:00
2007-12-22 20:42:36 +03:00
if ( ret < 0 ) {
2010-02-17 13:30:23 +03:00
netdev_err ( dev - > net , " Error reading Medium Status register: %02x \n " ,
ret ) ;
2011-10-04 13:55:18 +04:00
return ret ; /* TODO: callers not checking for error ret */
2005-08-31 20:53:10 +04:00
}
2011-10-04 13:55:18 +04:00
return le16_to_cpu ( v ) ;
2005-08-31 20:53:10 +04:00
}
2012-07-13 09:26:30 +04:00
int asix_write_medium_mode ( struct usbnet * dev , u16 mode )
2005-08-31 20:53:10 +04:00
{
2006-07-29 18:12:50 +04:00
int ret ;
2005-08-31 20:53:10 +04:00
2010-02-17 13:30:23 +03:00
netdev_dbg ( dev - > net , " asix_write_medium_mode() - mode = 0x%04x \n " , mode ) ;
2006-07-29 18:12:50 +04:00
ret = asix_write_cmd ( dev , AX_CMD_WRITE_MEDIUM_MODE , mode , 0 , 0 , NULL ) ;
if ( ret < 0 )
2010-02-17 13:30:23 +03:00
netdev_err ( dev - > net , " Failed to write Medium Mode mode to 0x%04x: %02x \n " ,
mode , ret ) ;
2005-08-31 20:53:10 +04:00
2006-07-29 18:12:50 +04:00
return ret ;
}
2005-08-31 20:53:10 +04:00
2012-07-13 09:26:30 +04:00
int asix_write_gpio ( struct usbnet * dev , u16 value , int sleep )
2006-07-29 18:12:50 +04:00
{
int ret ;
2005-08-31 20:53:10 +04:00
2010-02-17 13:30:23 +03:00
netdev_dbg ( dev - > net , " asix_write_gpio() - value = 0x%04x \n " , value ) ;
2006-07-29 18:12:50 +04:00
ret = asix_write_cmd ( dev , AX_CMD_WRITE_GPIOS , value , 0 , 0 , NULL ) ;
if ( ret < 0 )
2010-02-17 13:30:23 +03:00
netdev_err ( dev - > net , " Failed to write GPIO value 0x%04x: %02x \n " ,
value , ret ) ;
2005-08-31 20:53:10 +04:00
2006-07-29 18:12:50 +04:00
if ( sleep )
msleep ( sleep ) ;
return ret ;
2005-08-31 20:53:10 +04:00
}
2006-07-29 18:12:50 +04:00
/*
* AX88772 & AX88178 have a 16 - bit RX_CTL value
*/
2012-07-13 09:26:30 +04:00
void asix_set_multicast ( struct net_device * net )
2005-08-31 20:53:10 +04:00
{
struct usbnet * dev = netdev_priv ( net ) ;
2006-03-29 05:15:42 +04:00
struct asix_data * data = ( struct asix_data * ) & dev - > data ;
2006-07-29 18:12:50 +04:00
u16 rx_ctl = AX_DEFAULT_RX_CTL ;
2005-08-31 20:53:10 +04:00
if ( net - > flags & IFF_PROMISC ) {
2006-07-29 18:12:50 +04:00
rx_ctl | = AX_RX_CTL_PRO ;
2009-12-03 10:58:21 +03:00
} else if ( net - > flags & IFF_ALLMULTI | |
2010-02-08 07:30:35 +03:00
netdev_mc_count ( net ) > AX_MAX_MCAST ) {
2006-07-29 18:12:50 +04:00
rx_ctl | = AX_RX_CTL_AMALL ;
2010-02-08 07:30:35 +03:00
} else if ( netdev_mc_empty ( net ) ) {
2005-08-31 20:53:10 +04:00
/* just broadcast and directed */
} else {
/* We use the 20 byte dev->data
* for our 8 byte filter buffer
* to avoid allocating memory that
* is tricky to free later */
2010-04-02 01:22:57 +04:00
struct netdev_hw_addr * ha ;
2005-08-31 20:53:10 +04:00
u32 crc_bits ;
memset ( data - > multi_filter , 0 , AX_MCAST_FILTER_SIZE ) ;
/* Build the multicast hash filter. */
2010-04-02 01:22:57 +04:00
netdev_for_each_mc_addr ( ha , net ) {
crc_bits = ether_crc ( ETH_ALEN , ha - > addr ) > > 26 ;
2005-08-31 20:53:10 +04:00
data - > multi_filter [ crc_bits > > 3 ] | =
1 < < ( crc_bits & 7 ) ;
}
2006-03-29 05:15:42 +04:00
asix_write_cmd_async ( dev , AX_CMD_WRITE_MULTI_FILTER , 0 , 0 ,
2005-08-31 20:53:10 +04:00
AX_MCAST_FILTER_SIZE , data - > multi_filter ) ;
2006-07-29 18:12:50 +04:00
rx_ctl | = AX_RX_CTL_AM ;
2005-08-31 20:53:10 +04:00
}
2006-03-29 05:15:42 +04:00
asix_write_cmd_async ( dev , AX_CMD_WRITE_RX_CTL , rx_ctl , 0 , 0 , NULL ) ;
2005-08-31 20:53:10 +04:00
}
2012-07-13 09:26:30 +04:00
int asix_mdio_read ( struct net_device * netdev , int phy_id , int loc )
2005-08-31 20:53:10 +04:00
{
struct usbnet * dev = netdev_priv ( netdev ) ;
2007-12-22 20:42:36 +03:00
__le16 res ;
2005-08-31 20:53:10 +04:00
2006-10-09 02:08:02 +04:00
mutex_lock ( & dev - > phy_mutex ) ;
2006-03-29 05:15:42 +04:00
asix_set_sw_mii ( dev ) ;
asix_read_cmd ( dev , AX_CMD_READ_MII_REG , phy_id ,
2007-12-22 20:42:36 +03:00
( __u16 ) loc , 2 , & res ) ;
2006-03-29 05:15:42 +04:00
asix_set_hw_mii ( dev ) ;
2006-10-09 02:08:02 +04:00
mutex_unlock ( & dev - > phy_mutex ) ;
2005-08-31 20:53:10 +04:00
2010-02-17 13:30:23 +03:00
netdev_dbg ( dev - > net , " asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x \n " ,
phy_id , loc , le16_to_cpu ( res ) ) ;
2005-08-31 20:53:10 +04:00
2007-12-22 20:42:36 +03:00
return le16_to_cpu ( res ) ;
2005-08-31 20:53:10 +04:00
}
2012-07-13 09:26:30 +04:00
void asix_mdio_write ( struct net_device * netdev , int phy_id , int loc , int val )
2005-08-31 20:53:10 +04:00
{
struct usbnet * dev = netdev_priv ( netdev ) ;
2007-12-22 20:42:36 +03:00
__le16 res = cpu_to_le16 ( val ) ;
2005-08-31 20:53:10 +04:00
2010-02-17 13:30:23 +03:00
netdev_dbg ( dev - > net , " asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x \n " ,
phy_id , loc , val ) ;
2006-10-09 02:08:02 +04:00
mutex_lock ( & dev - > phy_mutex ) ;
2006-03-29 05:15:42 +04:00
asix_set_sw_mii ( dev ) ;
2007-12-22 20:42:36 +03:00
asix_write_cmd ( dev , AX_CMD_WRITE_MII_REG , phy_id , ( __u16 ) loc , 2 , & res ) ;
2006-03-29 05:15:42 +04:00
asix_set_hw_mii ( dev ) ;
2006-10-09 02:08:02 +04:00
mutex_unlock ( & dev - > phy_mutex ) ;
2005-08-31 20:53:10 +04:00
}
2012-07-13 09:26:30 +04:00
void asix_get_wol ( struct net_device * net , struct ethtool_wolinfo * wolinfo )
2005-08-31 20:53:10 +04:00
{
struct usbnet * dev = netdev_priv ( net ) ;
u8 opt ;
2006-03-29 05:15:42 +04:00
if ( asix_read_cmd ( dev , AX_CMD_READ_MONITOR_MODE , 0 , 0 , 1 , & opt ) < 0 ) {
2005-08-31 20:53:10 +04:00
wolinfo - > supported = 0 ;
wolinfo - > wolopts = 0 ;
return ;
}
wolinfo - > supported = WAKE_PHY | WAKE_MAGIC ;
wolinfo - > wolopts = 0 ;
2011-12-23 00:38:51 +04:00
if ( opt & AX_MONITOR_LINK )
wolinfo - > wolopts | = WAKE_PHY ;
if ( opt & AX_MONITOR_MAGIC )
wolinfo - > wolopts | = WAKE_MAGIC ;
2005-08-31 20:53:10 +04:00
}
2012-07-13 09:26:30 +04:00
int asix_set_wol ( struct net_device * net , struct ethtool_wolinfo * wolinfo )
2005-08-31 20:53:10 +04:00
{
struct usbnet * dev = netdev_priv ( net ) ;
u8 opt = 0 ;
if ( wolinfo - > wolopts & WAKE_PHY )
opt | = AX_MONITOR_LINK ;
if ( wolinfo - > wolopts & WAKE_MAGIC )
opt | = AX_MONITOR_MAGIC ;
2006-03-29 05:15:42 +04:00
if ( asix_write_cmd ( dev , AX_CMD_WRITE_MONITOR_MODE ,
2007-12-22 20:42:36 +03:00
opt , 0 , 0 , NULL ) < 0 )
2005-08-31 20:53:10 +04:00
return - EINVAL ;
return 0 ;
}
2012-07-13 09:26:30 +04:00
int asix_get_eeprom_len ( struct net_device * net )
2005-08-31 20:53:10 +04:00
{
2012-07-19 04:23:06 +04:00
return AX_EEPROM_LEN ;
2005-08-31 20:53:10 +04:00
}
2012-07-13 09:26:30 +04:00
int asix_get_eeprom ( struct net_device * net , struct ethtool_eeprom * eeprom ,
u8 * data )
2005-08-31 20:53:10 +04:00
{
struct usbnet * dev = netdev_priv ( net ) ;
2012-07-19 04:23:06 +04:00
u16 * eeprom_buff ;
int first_word , last_word ;
2005-08-31 20:53:10 +04:00
int i ;
2012-07-19 04:23:06 +04:00
if ( eeprom - > len = = 0 )
2005-08-31 20:53:10 +04:00
return - EINVAL ;
eeprom - > magic = AX_EEPROM_MAGIC ;
2012-07-19 04:23:06 +04:00
first_word = eeprom - > offset > > 1 ;
last_word = ( eeprom - > offset + eeprom - > len - 1 ) > > 1 ;
eeprom_buff = kmalloc ( sizeof ( u16 ) * ( last_word - first_word + 1 ) ,
GFP_KERNEL ) ;
if ( ! eeprom_buff )
return - ENOMEM ;
2005-08-31 20:53:10 +04:00
/* ax8817x returns 2 bytes from eeprom on read */
2012-07-19 04:23:06 +04:00
for ( i = first_word ; i < = last_word ; i + + ) {
if ( asix_read_cmd ( dev , AX_CMD_READ_EEPROM , i , 0 , 2 ,
& ( eeprom_buff [ i - first_word ] ) ) < 0 ) {
kfree ( eeprom_buff ) ;
return - EIO ;
}
2005-08-31 20:53:10 +04:00
}
2012-07-19 04:23:06 +04:00
memcpy ( data , ( u8 * ) eeprom_buff + ( eeprom - > offset & 1 ) , eeprom - > len ) ;
kfree ( eeprom_buff ) ;
2005-08-31 20:53:10 +04:00
return 0 ;
}
2012-07-19 04:23:07 +04:00
int asix_set_eeprom ( struct net_device * net , struct ethtool_eeprom * eeprom ,
u8 * data )
{
struct usbnet * dev = netdev_priv ( net ) ;
u16 * eeprom_buff ;
int first_word , last_word ;
int i ;
int ret ;
netdev_dbg ( net , " write EEPROM len %d, offset %d, magic 0x%x \n " ,
eeprom - > len , eeprom - > offset , eeprom - > magic ) ;
if ( eeprom - > len = = 0 )
return - EINVAL ;
if ( eeprom - > magic ! = AX_EEPROM_MAGIC )
return - EINVAL ;
first_word = eeprom - > offset > > 1 ;
last_word = ( eeprom - > offset + eeprom - > len - 1 ) > > 1 ;
eeprom_buff = kmalloc ( sizeof ( u16 ) * ( last_word - first_word + 1 ) ,
GFP_KERNEL ) ;
if ( ! eeprom_buff )
return - ENOMEM ;
/* align data to 16 bit boundaries, read the missing data from
the EEPROM */
if ( eeprom - > offset & 1 ) {
ret = asix_read_cmd ( dev , AX_CMD_READ_EEPROM , first_word , 0 , 2 ,
& ( eeprom_buff [ 0 ] ) ) ;
if ( ret < 0 ) {
netdev_err ( net , " Failed to read EEPROM at offset 0x%02x. \n " , first_word ) ;
goto free ;
}
}
if ( ( eeprom - > offset + eeprom - > len ) & 1 ) {
ret = asix_read_cmd ( dev , AX_CMD_READ_EEPROM , last_word , 0 , 2 ,
& ( eeprom_buff [ last_word - first_word ] ) ) ;
if ( ret < 0 ) {
netdev_err ( net , " Failed to read EEPROM at offset 0x%02x. \n " , last_word ) ;
goto free ;
}
}
memcpy ( ( u8 * ) eeprom_buff + ( eeprom - > offset & 1 ) , data , eeprom - > len ) ;
/* write data to EEPROM */
ret = asix_write_cmd ( dev , AX_CMD_WRITE_ENABLE , 0x0000 , 0 , 0 , NULL ) ;
if ( ret < 0 ) {
netdev_err ( net , " Failed to enable EEPROM write \n " ) ;
goto free ;
}
msleep ( 20 ) ;
for ( i = first_word ; i < = last_word ; i + + ) {
netdev_dbg ( net , " write to EEPROM at offset 0x%02x, data 0x%04x \n " ,
i , eeprom_buff [ i - first_word ] ) ;
ret = asix_write_cmd ( dev , AX_CMD_WRITE_EEPROM , i ,
eeprom_buff [ i - first_word ] , 0 , NULL ) ;
if ( ret < 0 ) {
netdev_err ( net , " Failed to write EEPROM at offset 0x%02x. \n " ,
i ) ;
goto free ;
}
msleep ( 20 ) ;
}
ret = asix_write_cmd ( dev , AX_CMD_WRITE_DISABLE , 0x0000 , 0 , 0 , NULL ) ;
if ( ret < 0 ) {
netdev_err ( net , " Failed to disable EEPROM write \n " ) ;
goto free ;
}
ret = 0 ;
free :
kfree ( eeprom_buff ) ;
return ret ;
}
2012-07-13 09:26:30 +04:00
void asix_get_drvinfo ( struct net_device * net , struct ethtool_drvinfo * info )
2005-08-31 20:53:10 +04:00
{
/* Inherit standard device info */
usbnet_get_drvinfo ( net , info ) ;
2013-01-06 04:44:26 +04:00
strlcpy ( info - > driver , DRIVER_NAME , sizeof ( info - > driver ) ) ;
strlcpy ( info - > version , DRIVER_VERSION , sizeof ( info - > version ) ) ;
2005-08-31 20:53:10 +04:00
}
2012-07-13 09:26:30 +04:00
int asix_set_mac_address ( struct net_device * net , void * p )
2010-03-09 15:24:38 +03:00
{
struct usbnet * dev = netdev_priv ( net ) ;
struct asix_data * data = ( struct asix_data * ) & dev - > data ;
struct sockaddr * addr = p ;
if ( netif_running ( net ) )
return - EBUSY ;
if ( ! is_valid_ether_addr ( addr - > sa_data ) )
return - EADDRNOTAVAIL ;
memcpy ( net - > dev_addr , addr - > sa_data , ETH_ALEN ) ;
/* We use the 20 byte dev->data
* for our 6 byte mac buffer
* to avoid allocating memory that
* is tricky to free later */
memcpy ( data - > mac_addr , addr - > sa_data , ETH_ALEN ) ;
asix_write_cmd_async ( dev , AX_CMD_WRITE_NODE_ID , 0 , 0 , ETH_ALEN ,
data - > mac_addr ) ;
return 0 ;
}