2017-07-25 17:03:18 +03:00
# include <linux/export.h>
# include <linux/kref.h>
# include <linux/list.h>
# include <linux/mutex.h>
# include <linux/phylink.h>
# include <linux/rtnetlink.h>
# include <linux/slab.h>
# include "sfp.h"
2017-12-01 13:24:53 +03:00
/**
* struct sfp_bus - internal representation of a sfp bus
*/
2017-07-25 17:03:18 +03:00
struct sfp_bus {
2017-12-01 13:24:53 +03:00
/* private: */
2017-07-25 17:03:18 +03:00
struct kref kref ;
struct list_head node ;
2017-12-01 13:25:03 +03:00
struct fwnode_handle * fwnode ;
2017-07-25 17:03:18 +03:00
const struct sfp_socket_ops * socket_ops ;
struct device * sfp_dev ;
struct sfp * sfp ;
const struct sfp_upstream_ops * upstream_ops ;
void * upstream ;
struct net_device * netdev ;
struct phy_device * phydev ;
bool registered ;
bool started ;
} ;
2017-12-01 13:24:53 +03:00
/**
* sfp_parse_port ( ) - Parse the EEPROM base ID , setting the port type
* @ bus : a pointer to the & struct sfp_bus structure for the sfp module
* @ id : a pointer to the module ' s & struct sfp_eeprom_id
* @ support : optional pointer to an array of unsigned long for the
* ethtool support mask
*
* Parse the EEPROM identification given in @ id , and return one of
* % PORT_TP , % PORT_FIBRE or % PORT_OTHER . If @ support is non - % NULL ,
* also set the ethtool % ETHTOOL_LINK_MODE_xxx_BIT corresponding with
* the connector type .
*
* If the port type is not known , returns % PORT_OTHER .
*/
2017-07-25 17:03:18 +03:00
int sfp_parse_port ( struct sfp_bus * bus , const struct sfp_eeprom_id * id ,
unsigned long * support )
{
int port ;
/* port is the physical connector, set this from the connector field. */
switch ( id - > base . connector ) {
case SFP_CONNECTOR_SC :
case SFP_CONNECTOR_FIBERJACK :
case SFP_CONNECTOR_LC :
case SFP_CONNECTOR_MT_RJ :
case SFP_CONNECTOR_MU :
case SFP_CONNECTOR_OPTICAL_PIGTAIL :
port = PORT_FIBRE ;
break ;
case SFP_CONNECTOR_RJ45 :
port = PORT_TP ;
break ;
2017-12-29 15:15:28 +03:00
case SFP_CONNECTOR_COPPER_PIGTAIL :
port = PORT_DA ;
break ;
2017-07-25 17:03:18 +03:00
case SFP_CONNECTOR_UNSPEC :
if ( id - > base . e1000_base_t ) {
port = PORT_TP ;
break ;
}
/* fallthrough */
case SFP_CONNECTOR_SG : /* guess */
case SFP_CONNECTOR_MPO_1X12 :
case SFP_CONNECTOR_MPO_2X16 :
case SFP_CONNECTOR_HSSDC_II :
case SFP_CONNECTOR_NOSEPARATE :
case SFP_CONNECTOR_MXC_2X16 :
port = PORT_OTHER ;
break ;
default :
dev_warn ( bus - > sfp_dev , " SFP: unknown connector id 0x%02x \n " ,
id - > base . connector ) ;
port = PORT_OTHER ;
break ;
}
2017-12-29 15:15:28 +03:00
if ( support ) {
switch ( port ) {
case PORT_FIBRE :
phylink_set ( support , FIBRE ) ;
break ;
case PORT_TP :
phylink_set ( support , TP ) ;
break ;
}
}
2017-07-25 17:03:18 +03:00
return port ;
}
EXPORT_SYMBOL_GPL ( sfp_parse_port ) ;
2017-12-01 13:24:53 +03:00
/**
* sfp_parse_interface ( ) - Parse the phy_interface_t
* @ bus : a pointer to the & struct sfp_bus structure for the sfp module
* @ id : a pointer to the module ' s & struct sfp_eeprom_id
*
* Derive the phy_interface_t mode for the information found in the
* module ' s identifying EEPROM . There is no standard or defined way
* to derive this information , so we use some heuristics .
*
* If the encoding is 64 b66b , then the module must be > = 10 G , so
* return % PHY_INTERFACE_MODE_10GKR .
*
* If it ' s 8 b10b , then it ' s 1 G or slower . If it ' s definitely a fibre
* module , return % PHY_INTERFACE_MODE_1000BASEX mode , otherwise return
* % PHY_INTERFACE_MODE_SGMII mode .
*
* If the encoding is not known , return % PHY_INTERFACE_MODE_NA .
*/
2017-07-25 17:03:18 +03:00
phy_interface_t sfp_parse_interface ( struct sfp_bus * bus ,
const struct sfp_eeprom_id * id )
{
phy_interface_t iface ;
/* Setting the serdes link mode is guesswork: there's no field in
* the EEPROM which indicates what mode should be used .
*
* If the module wants 64 b66b , then it must be > = 10 G .
*
* If it ' s a gigabit - only fiber module , it probably does not have
* a PHY , so switch to 802.3 z negotiation mode . Otherwise , switch
* to SGMII mode ( which is required to support non - gigabit speeds ) .
*/
switch ( id - > base . encoding ) {
case SFP_ENCODING_8472_64B66B :
iface = PHY_INTERFACE_MODE_10GKR ;
break ;
case SFP_ENCODING_8B10B :
if ( ! id - > base . e1000_base_t & &
! id - > base . e100_base_lx & &
! id - > base . e100_base_fx )
iface = PHY_INTERFACE_MODE_1000BASEX ;
else
iface = PHY_INTERFACE_MODE_SGMII ;
break ;
default :
2017-12-29 15:15:28 +03:00
if ( id - > base . e1000_base_cx ) {
iface = PHY_INTERFACE_MODE_1000BASEX ;
break ;
}
2017-07-25 17:03:18 +03:00
iface = PHY_INTERFACE_MODE_NA ;
dev_err ( bus - > sfp_dev ,
" SFP module encoding does not support 8b10b nor 64b66b \n " ) ;
break ;
}
return iface ;
}
EXPORT_SYMBOL_GPL ( sfp_parse_interface ) ;
2017-12-01 13:24:53 +03:00
/**
* sfp_parse_support ( ) - Parse the eeprom id for supported link modes
* @ bus : a pointer to the & struct sfp_bus structure for the sfp module
* @ id : a pointer to the module ' s & struct sfp_eeprom_id
* @ support : pointer to an array of unsigned long for the ethtool support mask
*
* Parse the EEPROM identification information and derive the supported
* ethtool link modes for the module .
*/
2017-07-25 17:03:18 +03:00
void sfp_parse_support ( struct sfp_bus * bus , const struct sfp_eeprom_id * id ,
unsigned long * support )
{
2017-12-29 15:15:23 +03:00
unsigned int br_min , br_nom , br_max ;
2018-02-27 18:52:57 +03:00
__ETHTOOL_DECLARE_LINK_MODE_MASK ( modes ) = { 0 , } ;
2017-07-25 17:03:18 +03:00
2017-12-29 15:15:23 +03:00
/* Decode the bitrate information to MBd */
br_min = br_nom = br_max = 0 ;
if ( id - > base . br_nominal ) {
if ( id - > base . br_nominal ! = 255 ) {
br_nom = id - > base . br_nominal * 100 ;
br_min = br_nom + id - > base . br_nominal * id - > ext . br_min ;
br_max = br_nom + id - > base . br_nominal * id - > ext . br_max ;
} else if ( id - > ext . br_max ) {
br_nom = 250 * id - > ext . br_max ;
br_max = br_nom + br_nom * id - > ext . br_min / 100 ;
br_min = br_nom - br_nom * id - > ext . br_min / 100 ;
}
}
2017-07-25 17:03:18 +03:00
/* Set ethtool support from the compliance fields. */
if ( id - > base . e10g_base_sr )
2018-02-27 18:52:57 +03:00
phylink_set ( modes , 10000 baseSR_Full ) ;
2017-07-25 17:03:18 +03:00
if ( id - > base . e10g_base_lr )
2018-02-27 18:52:57 +03:00
phylink_set ( modes , 10000 baseLR_Full ) ;
2017-07-25 17:03:18 +03:00
if ( id - > base . e10g_base_lrm )
2018-02-27 18:52:57 +03:00
phylink_set ( modes , 10000 baseLRM_Full ) ;
2017-07-25 17:03:18 +03:00
if ( id - > base . e10g_base_er )
2018-02-27 18:52:57 +03:00
phylink_set ( modes , 10000 baseER_Full ) ;
2017-07-25 17:03:18 +03:00
if ( id - > base . e1000_base_sx | |
id - > base . e1000_base_lx | |
id - > base . e1000_base_cx )
2018-02-27 18:52:57 +03:00
phylink_set ( modes , 1000 baseX_Full ) ;
2017-07-25 17:03:18 +03:00
if ( id - > base . e1000_base_t ) {
2018-02-27 18:52:57 +03:00
phylink_set ( modes , 1000 baseT_Half ) ;
phylink_set ( modes , 1000 baseT_Full ) ;
2017-07-25 17:03:18 +03:00
}
2017-12-29 15:15:23 +03:00
/* 1000Base-PX or 1000Base-BX10 */
if ( ( id - > base . e_base_px | | id - > base . e_base_bx10 ) & &
br_min < = 1300 & & br_max > = 1200 )
phylink_set ( support , 1000 baseX_Full ) ;
2017-12-29 15:15:28 +03:00
/* For active or passive cables, select the link modes
* based on the bit rates and the cable compliance bytes .
*/
if ( ( id - > base . sfp_ct_passive | | id - > base . sfp_ct_active ) & & br_nom ) {
/* This may look odd, but some manufacturers use 12000MBd */
if ( br_min < = 12000 & & br_max > = 10300 )
2018-02-27 18:52:57 +03:00
phylink_set ( modes , 10000 baseCR_Full ) ;
2017-12-29 15:15:28 +03:00
if ( br_min < = 3200 & & br_max > = 3100 )
2018-02-27 18:52:57 +03:00
phylink_set ( modes , 2500 baseX_Full ) ;
2017-12-29 15:15:28 +03:00
if ( br_min < = 1300 & & br_max > = 1200 )
2018-02-27 18:52:57 +03:00
phylink_set ( modes , 1000 baseX_Full ) ;
2017-12-29 15:15:28 +03:00
}
if ( id - > base . sfp_ct_passive ) {
if ( id - > base . passive . sff8431_app_e )
2018-02-27 18:52:57 +03:00
phylink_set ( modes , 10000 baseCR_Full ) ;
2017-12-29 15:15:28 +03:00
}
if ( id - > base . sfp_ct_active ) {
if ( id - > base . active . sff8431_app_e | |
id - > base . active . sff8431_lim ) {
2018-02-27 18:52:57 +03:00
phylink_set ( modes , 10000 baseCR_Full ) ;
2017-12-29 15:15:28 +03:00
}
}
2017-07-25 17:03:18 +03:00
switch ( id - > base . extended_cc ) {
case 0x00 : /* Unspecified */
break ;
case 0x02 : /* 100Gbase-SR4 or 25Gbase-SR */
2018-02-27 18:52:57 +03:00
phylink_set ( modes , 100000 baseSR4_Full ) ;
phylink_set ( modes , 25000 baseSR_Full ) ;
2017-07-25 17:03:18 +03:00
break ;
case 0x03 : /* 100Gbase-LR4 or 25Gbase-LR */
case 0x04 : /* 100Gbase-ER4 or 25Gbase-ER */
2018-02-27 18:52:57 +03:00
phylink_set ( modes , 100000 baseLR4_ER4_Full ) ;
2017-07-25 17:03:18 +03:00
break ;
case 0x0b : /* 100Gbase-CR4 or 25Gbase-CR CA-L */
case 0x0c : /* 25Gbase-CR CA-S */
case 0x0d : /* 25Gbase-CR CA-N */
2018-02-27 18:52:57 +03:00
phylink_set ( modes , 100000 baseCR4_Full ) ;
phylink_set ( modes , 25000 baseCR_Full ) ;
2017-07-25 17:03:18 +03:00
break ;
default :
dev_warn ( bus - > sfp_dev ,
" Unknown/unsupported extended compliance code: 0x%02x \n " ,
id - > base . extended_cc ) ;
break ;
}
/* For fibre channel SFP, derive possible BaseX modes */
if ( id - > base . fc_speed_100 | |
id - > base . fc_speed_200 | |
id - > base . fc_speed_400 ) {
if ( id - > base . br_nominal > = 31 )
2018-02-27 18:52:57 +03:00
phylink_set ( modes , 2500 baseX_Full ) ;
2017-07-25 17:03:18 +03:00
if ( id - > base . br_nominal > = 12 )
2018-02-27 18:52:57 +03:00
phylink_set ( modes , 1000 baseX_Full ) ;
2017-07-25 17:03:18 +03:00
}
2018-02-27 18:52:57 +03:00
/* If we haven't discovered any modes that this module supports, try
* the encoding and bitrate to determine supported modes . Some BiDi
* modules ( eg , 1310 nm / 1550 nm ) are not 1000 BASE - BX compliant due to
* the differing wavelengths , so do not set any transceiver bits .
*/
if ( bitmap_empty ( modes , __ETHTOOL_LINK_MODE_MASK_NBITS ) ) {
/* If the encoding and bit rate allows 1000baseX */
if ( id - > base . encoding = = SFP_ENCODING_8B10B & & br_nom & &
br_min < = 1300 & & br_max > = 1200 )
phylink_set ( modes , 1000 baseX_Full ) ;
}
bitmap_or ( support , support , modes , __ETHTOOL_LINK_MODE_MASK_NBITS ) ;
phylink_set ( support , Autoneg ) ;
phylink_set ( support , Pause ) ;
phylink_set ( support , Asym_Pause ) ;
2017-07-25 17:03:18 +03:00
}
EXPORT_SYMBOL_GPL ( sfp_parse_support ) ;
static LIST_HEAD ( sfp_buses ) ;
static DEFINE_MUTEX ( sfp_mutex ) ;
static const struct sfp_upstream_ops * sfp_get_upstream_ops ( struct sfp_bus * bus )
{
return bus - > registered ? bus - > upstream_ops : NULL ;
}
2017-12-01 13:25:03 +03:00
static struct sfp_bus * sfp_bus_get ( struct fwnode_handle * fwnode )
2017-07-25 17:03:18 +03:00
{
struct sfp_bus * sfp , * new , * found = NULL ;
new = kzalloc ( sizeof ( * new ) , GFP_KERNEL ) ;
mutex_lock ( & sfp_mutex ) ;
list_for_each_entry ( sfp , & sfp_buses , node ) {
2017-12-01 13:25:03 +03:00
if ( sfp - > fwnode = = fwnode ) {
2017-07-25 17:03:18 +03:00
kref_get ( & sfp - > kref ) ;
found = sfp ;
break ;
}
}
if ( ! found & & new ) {
kref_init ( & new - > kref ) ;
2017-12-01 13:25:03 +03:00
new - > fwnode = fwnode ;
2017-07-25 17:03:18 +03:00
list_add ( & new - > node , & sfp_buses ) ;
found = new ;
new = NULL ;
}
mutex_unlock ( & sfp_mutex ) ;
kfree ( new ) ;
return found ;
}
2017-12-01 13:24:58 +03:00
static void sfp_bus_release ( struct kref * kref )
2017-07-25 17:03:18 +03:00
{
struct sfp_bus * bus = container_of ( kref , struct sfp_bus , kref ) ;
list_del ( & bus - > node ) ;
mutex_unlock ( & sfp_mutex ) ;
kfree ( bus ) ;
}
static void sfp_bus_put ( struct sfp_bus * bus )
{
kref_put_mutex ( & bus - > kref , sfp_bus_release , & sfp_mutex ) ;
}
static int sfp_register_bus ( struct sfp_bus * bus )
{
const struct sfp_upstream_ops * ops = bus - > upstream_ops ;
int ret ;
if ( ops ) {
if ( ops - > link_down )
ops - > link_down ( bus - > upstream ) ;
if ( ops - > connect_phy & & bus - > phydev ) {
ret = ops - > connect_phy ( bus - > upstream , bus - > phydev ) ;
if ( ret )
return ret ;
}
}
if ( bus - > started )
bus - > socket_ops - > start ( bus - > sfp ) ;
bus - > registered = true ;
return 0 ;
}
static void sfp_unregister_bus ( struct sfp_bus * bus )
{
const struct sfp_upstream_ops * ops = bus - > upstream_ops ;
if ( bus - > registered ) {
if ( bus - > started )
bus - > socket_ops - > stop ( bus - > sfp ) ;
if ( bus - > phydev & & ops & & ops - > disconnect_phy )
ops - > disconnect_phy ( bus - > upstream ) ;
}
bus - > registered = false ;
}
2017-12-01 13:24:53 +03:00
/**
* sfp_get_module_info ( ) - Get the ethtool_modinfo for a SFP module
* @ bus : a pointer to the & struct sfp_bus structure for the sfp module
* @ modinfo : a & struct ethtool_modinfo
*
* Fill in the type and eeprom_len parameters in @ modinfo for a module on
* the sfp bus specified by @ bus .
*
* Returns 0 on success or a negative errno number .
*/
2017-07-25 17:03:18 +03:00
int sfp_get_module_info ( struct sfp_bus * bus , struct ethtool_modinfo * modinfo )
{
if ( ! bus - > registered )
return - ENOIOCTLCMD ;
return bus - > socket_ops - > module_info ( bus - > sfp , modinfo ) ;
}
EXPORT_SYMBOL_GPL ( sfp_get_module_info ) ;
2017-12-01 13:24:53 +03:00
/**
* sfp_get_module_eeprom ( ) - Read the SFP module EEPROM
* @ bus : a pointer to the & struct sfp_bus structure for the sfp module
* @ ee : a & struct ethtool_eeprom
* @ data : buffer to contain the EEPROM data ( must be at least @ ee - > len bytes )
*
* Read the EEPROM as specified by the supplied @ ee . See the documentation
* for & struct ethtool_eeprom for the region to be read .
*
* Returns 0 on success or a negative errno number .
*/
2017-07-25 17:03:18 +03:00
int sfp_get_module_eeprom ( struct sfp_bus * bus , struct ethtool_eeprom * ee ,
2017-10-31 07:42:57 +03:00
u8 * data )
2017-07-25 17:03:18 +03:00
{
if ( ! bus - > registered )
return - ENOIOCTLCMD ;
return bus - > socket_ops - > module_eeprom ( bus - > sfp , ee , data ) ;
}
EXPORT_SYMBOL_GPL ( sfp_get_module_eeprom ) ;
2017-12-01 13:24:53 +03:00
/**
* sfp_upstream_start ( ) - Inform the SFP that the network device is up
* @ bus : a pointer to the & struct sfp_bus structure for the sfp module
*
* Inform the SFP socket that the network device is now up , so that the
* module can be enabled by allowing TX_DISABLE to be deasserted . This
* should be called from the network device driver ' s & struct net_device_ops
* ndo_open ( ) method .
*/
2017-07-25 17:03:18 +03:00
void sfp_upstream_start ( struct sfp_bus * bus )
{
if ( bus - > registered )
bus - > socket_ops - > start ( bus - > sfp ) ;
bus - > started = true ;
}
EXPORT_SYMBOL_GPL ( sfp_upstream_start ) ;
2017-12-01 13:24:53 +03:00
/**
* sfp_upstream_stop ( ) - Inform the SFP that the network device is down
* @ bus : a pointer to the & struct sfp_bus structure for the sfp module
*
* Inform the SFP socket that the network device is now up , so that the
* module can be disabled by asserting TX_DISABLE , disabling the laser
* in optical modules . This should be called from the network device
* driver ' s & struct net_device_ops ndo_stop ( ) method .
*/
2017-07-25 17:03:18 +03:00
void sfp_upstream_stop ( struct sfp_bus * bus )
{
if ( bus - > registered )
bus - > socket_ops - > stop ( bus - > sfp ) ;
bus - > started = false ;
}
EXPORT_SYMBOL_GPL ( sfp_upstream_stop ) ;
2017-12-01 13:24:53 +03:00
/**
* sfp_register_upstream ( ) - Register the neighbouring device
2018-01-23 06:14:26 +03:00
* @ fwnode : firmware node for the SFP bus
2017-12-01 13:24:53 +03:00
* @ ndev : network device associated with the interface
* @ upstream : the upstream private data
* @ ops : the upstream ' s & struct sfp_upstream_ops
*
* Register the upstream device ( eg , PHY ) with the SFP bus . MAC drivers
* should use phylink , which will call this function for them . Returns
* a pointer to the allocated & struct sfp_bus .
*
* On error , returns % NULL .
*/
2017-12-01 13:25:03 +03:00
struct sfp_bus * sfp_register_upstream ( struct fwnode_handle * fwnode ,
2017-10-31 07:42:57 +03:00
struct net_device * ndev , void * upstream ,
const struct sfp_upstream_ops * ops )
2017-07-25 17:03:18 +03:00
{
2017-12-01 13:25:03 +03:00
struct sfp_bus * bus = sfp_bus_get ( fwnode ) ;
2017-07-25 17:03:18 +03:00
int ret = 0 ;
if ( bus ) {
rtnl_lock ( ) ;
bus - > upstream_ops = ops ;
bus - > upstream = upstream ;
bus - > netdev = ndev ;
if ( bus - > sfp )
ret = sfp_register_bus ( bus ) ;
rtnl_unlock ( ) ;
}
if ( ret ) {
sfp_bus_put ( bus ) ;
bus = NULL ;
}
return bus ;
}
EXPORT_SYMBOL_GPL ( sfp_register_upstream ) ;
2017-12-01 13:24:53 +03:00
/**
* sfp_unregister_upstream ( ) - Unregister sfp bus
* @ bus : a pointer to the & struct sfp_bus structure for the sfp module
*
* Unregister a previously registered upstream connection for the SFP
* module . @ bus is returned from sfp_register_upstream ( ) .
*/
2017-07-25 17:03:18 +03:00
void sfp_unregister_upstream ( struct sfp_bus * bus )
{
rtnl_lock ( ) ;
2017-12-27 02:15:17 +03:00
if ( bus - > sfp )
sfp_unregister_bus ( bus ) ;
2017-07-25 17:03:18 +03:00
bus - > upstream = NULL ;
bus - > netdev = NULL ;
rtnl_unlock ( ) ;
sfp_bus_put ( bus ) ;
}
EXPORT_SYMBOL_GPL ( sfp_unregister_upstream ) ;
/* Socket driver entry points */
int sfp_add_phy ( struct sfp_bus * bus , struct phy_device * phydev )
{
const struct sfp_upstream_ops * ops = sfp_get_upstream_ops ( bus ) ;
int ret = 0 ;
if ( ops & & ops - > connect_phy )
ret = ops - > connect_phy ( bus - > upstream , phydev ) ;
if ( ret = = 0 )
bus - > phydev = phydev ;
return ret ;
}
EXPORT_SYMBOL_GPL ( sfp_add_phy ) ;
void sfp_remove_phy ( struct sfp_bus * bus )
{
const struct sfp_upstream_ops * ops = sfp_get_upstream_ops ( bus ) ;
if ( ops & & ops - > disconnect_phy )
ops - > disconnect_phy ( bus - > upstream ) ;
bus - > phydev = NULL ;
}
EXPORT_SYMBOL_GPL ( sfp_remove_phy ) ;
void sfp_link_up ( struct sfp_bus * bus )
{
const struct sfp_upstream_ops * ops = sfp_get_upstream_ops ( bus ) ;
if ( ops & & ops - > link_up )
ops - > link_up ( bus - > upstream ) ;
}
EXPORT_SYMBOL_GPL ( sfp_link_up ) ;
void sfp_link_down ( struct sfp_bus * bus )
{
const struct sfp_upstream_ops * ops = sfp_get_upstream_ops ( bus ) ;
if ( ops & & ops - > link_down )
ops - > link_down ( bus - > upstream ) ;
}
EXPORT_SYMBOL_GPL ( sfp_link_down ) ;
int sfp_module_insert ( struct sfp_bus * bus , const struct sfp_eeprom_id * id )
{
const struct sfp_upstream_ops * ops = sfp_get_upstream_ops ( bus ) ;
int ret = 0 ;
if ( ops & & ops - > module_insert )
ret = ops - > module_insert ( bus - > upstream , id ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( sfp_module_insert ) ;
void sfp_module_remove ( struct sfp_bus * bus )
{
const struct sfp_upstream_ops * ops = sfp_get_upstream_ops ( bus ) ;
if ( ops & & ops - > module_remove )
ops - > module_remove ( bus - > upstream ) ;
}
EXPORT_SYMBOL_GPL ( sfp_module_remove ) ;
struct sfp_bus * sfp_register_socket ( struct device * dev , struct sfp * sfp ,
const struct sfp_socket_ops * ops )
{
2017-12-01 13:25:03 +03:00
struct sfp_bus * bus = sfp_bus_get ( dev - > fwnode ) ;
2017-07-25 17:03:18 +03:00
int ret = 0 ;
if ( bus ) {
rtnl_lock ( ) ;
bus - > sfp_dev = dev ;
bus - > sfp = sfp ;
bus - > socket_ops = ops ;
if ( bus - > netdev )
ret = sfp_register_bus ( bus ) ;
rtnl_unlock ( ) ;
}
if ( ret ) {
sfp_bus_put ( bus ) ;
bus = NULL ;
}
return bus ;
}
EXPORT_SYMBOL_GPL ( sfp_register_socket ) ;
void sfp_unregister_socket ( struct sfp_bus * bus )
{
rtnl_lock ( ) ;
2017-12-27 02:15:17 +03:00
if ( bus - > netdev )
sfp_unregister_bus ( bus ) ;
2017-07-25 17:03:18 +03:00
bus - > sfp_dev = NULL ;
bus - > sfp = NULL ;
bus - > socket_ops = NULL ;
rtnl_unlock ( ) ;
sfp_bus_put ( bus ) ;
}
EXPORT_SYMBOL_GPL ( sfp_unregister_socket ) ;