mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	Previously, the condition for invalidating the tx flow in mctp_i2c_invalidate_tx_flow() checked if `rc` was nonzero. However, this could incorrectly trigger the invalidation even when `rc > 0` was returned as a success status. This patch updates the condition to explicitly check for `rc < 0`, ensuring that only error cases trigger the invalidation. Signed-off-by: Daniel Hsu <Daniel-Hsu@quantatw.com> Reviewed-by: Jeremy Kerr <jk@codeconstruct.com.au> Signed-off-by: David S. Miller <davem@davemloft.net>
		
			
				
	
	
		
			1146 lines
		
	
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1146 lines
		
	
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0
 | 
						|
/*
 | 
						|
 * Management Controller Transport Protocol (MCTP)
 | 
						|
 * Implements DMTF specification
 | 
						|
 * "DSP0237 Management Component Transport Protocol (MCTP) SMBus/I2C
 | 
						|
 * Transport Binding"
 | 
						|
 * https://www.dmtf.org/sites/default/files/standards/documents/DSP0237_1.2.0.pdf
 | 
						|
 *
 | 
						|
 * A netdev is created for each I2C bus that handles MCTP. In the case of an I2C
 | 
						|
 * mux topology a single I2C client is attached to the root of the mux topology,
 | 
						|
 * shared between all mux I2C busses underneath. For non-mux cases an I2C client
 | 
						|
 * is attached per netdev.
 | 
						|
 *
 | 
						|
 * mctp-i2c-controller.yml devicetree binding has further details.
 | 
						|
 *
 | 
						|
 * Copyright (c) 2022 Code Construct
 | 
						|
 * Copyright (c) 2022 Google
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/module.h>
 | 
						|
#include <linux/netdevice.h>
 | 
						|
#include <linux/i2c.h>
 | 
						|
#include <linux/i2c-mux.h>
 | 
						|
#include <linux/if_arp.h>
 | 
						|
#include <net/mctp.h>
 | 
						|
#include <net/mctpdevice.h>
 | 
						|
 | 
						|
/* byte_count is limited to u8 */
 | 
						|
#define MCTP_I2C_MAXBLOCK 255
 | 
						|
/* One byte is taken by source_slave */
 | 
						|
#define MCTP_I2C_MAXMTU (MCTP_I2C_MAXBLOCK - 1)
 | 
						|
#define MCTP_I2C_MINMTU (64 + 4)
 | 
						|
/* Allow space for dest_address, command, byte_count, data, PEC */
 | 
						|
#define MCTP_I2C_BUFSZ (3 + MCTP_I2C_MAXBLOCK + 1)
 | 
						|
#define MCTP_I2C_MINLEN 8
 | 
						|
#define MCTP_I2C_COMMANDCODE 0x0f
 | 
						|
#define MCTP_I2C_TX_WORK_LEN 100
 | 
						|
/* Sufficient for 64kB at min mtu */
 | 
						|
#define MCTP_I2C_TX_QUEUE_LEN 1100
 | 
						|
 | 
						|
#define MCTP_I2C_OF_PROP "mctp-controller"
 | 
						|
 | 
						|
enum {
 | 
						|
	MCTP_I2C_FLOW_STATE_NEW = 0,
 | 
						|
	MCTP_I2C_FLOW_STATE_ACTIVE,
 | 
						|
	MCTP_I2C_FLOW_STATE_INVALID,
 | 
						|
};
 | 
						|
 | 
						|
/* List of all struct mctp_i2c_client
 | 
						|
 * Lock protects driver_clients and also prevents adding/removing adapters
 | 
						|
 * during mctp_i2c_client probe/remove.
 | 
						|
 */
 | 
						|
static DEFINE_MUTEX(driver_clients_lock);
 | 
						|
static LIST_HEAD(driver_clients);
 | 
						|
 | 
						|
struct mctp_i2c_client;
 | 
						|
 | 
						|
/* The netdev structure. One of these per I2C adapter. */
 | 
						|
struct mctp_i2c_dev {
 | 
						|
	struct net_device *ndev;
 | 
						|
	struct i2c_adapter *adapter;
 | 
						|
	struct mctp_i2c_client *client;
 | 
						|
	struct list_head list; /* For mctp_i2c_client.devs */
 | 
						|
 | 
						|
	size_t rx_pos;
 | 
						|
	u8 rx_buffer[MCTP_I2C_BUFSZ];
 | 
						|
	struct completion rx_done;
 | 
						|
 | 
						|
	struct task_struct *tx_thread;
 | 
						|
	wait_queue_head_t tx_wq;
 | 
						|
	struct sk_buff_head tx_queue;
 | 
						|
	u8 tx_scratch[MCTP_I2C_BUFSZ];
 | 
						|
 | 
						|
	/* A fake entry in our tx queue to perform an unlock operation */
 | 
						|
	struct sk_buff unlock_marker;
 | 
						|
 | 
						|
	/* Spinlock protects i2c_lock_count, release_count, allow_rx */
 | 
						|
	spinlock_t lock;
 | 
						|
	int i2c_lock_count;
 | 
						|
	int release_count;
 | 
						|
	/* Indicates that the netif is ready to receive incoming packets */
 | 
						|
	bool allow_rx;
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
/* The i2c client structure. One per hardware i2c bus at the top of the
 | 
						|
 * mux tree, shared by multiple netdevs
 | 
						|
 */
 | 
						|
struct mctp_i2c_client {
 | 
						|
	struct i2c_client *client;
 | 
						|
	u8 lladdr;
 | 
						|
 | 
						|
	struct mctp_i2c_dev *sel;
 | 
						|
	struct list_head devs;
 | 
						|
	spinlock_t sel_lock; /* Protects sel and devs */
 | 
						|
 | 
						|
	struct list_head list; /* For driver_clients */
 | 
						|
};
 | 
						|
 | 
						|
/* Header on the wire. */
 | 
						|
struct mctp_i2c_hdr {
 | 
						|
	u8 dest_slave;
 | 
						|
	u8 command;
 | 
						|
	/* Count of bytes following byte_count, excluding PEC */
 | 
						|
	u8 byte_count;
 | 
						|
	u8 source_slave;
 | 
						|
};
 | 
						|
 | 
						|
static int mctp_i2c_recv(struct mctp_i2c_dev *midev);
 | 
						|
static int mctp_i2c_slave_cb(struct i2c_client *client,
 | 
						|
			     enum i2c_slave_event event, u8 *val);
 | 
						|
static void mctp_i2c_ndo_uninit(struct net_device *dev);
 | 
						|
static int mctp_i2c_ndo_open(struct net_device *dev);
 | 
						|
 | 
						|
static struct i2c_adapter *mux_root_adapter(struct i2c_adapter *adap)
 | 
						|
{
 | 
						|
#if IS_ENABLED(CONFIG_I2C_MUX)
 | 
						|
	return i2c_root_adapter(&adap->dev);
 | 
						|
#else
 | 
						|
	/* In non-mux config all i2c adapters are root adapters */
 | 
						|
	return adap;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
/* Creates a new i2c slave device attached to the root adapter.
 | 
						|
 * Sets up the slave callback.
 | 
						|
 * Must be called with a client on a root adapter.
 | 
						|
 */
 | 
						|
static struct mctp_i2c_client *mctp_i2c_new_client(struct i2c_client *client)
 | 
						|
{
 | 
						|
	struct mctp_i2c_client *mcli = NULL;
 | 
						|
	struct i2c_adapter *root = NULL;
 | 
						|
	int rc;
 | 
						|
 | 
						|
	if (client->flags & I2C_CLIENT_TEN) {
 | 
						|
		dev_err(&client->dev, "failed, MCTP requires a 7-bit I2C address, addr=0x%x\n",
 | 
						|
			client->addr);
 | 
						|
		rc = -EINVAL;
 | 
						|
		goto err;
 | 
						|
	}
 | 
						|
 | 
						|
	root = mux_root_adapter(client->adapter);
 | 
						|
	if (!root) {
 | 
						|
		dev_err(&client->dev, "failed to find root adapter\n");
 | 
						|
		rc = -ENOENT;
 | 
						|
		goto err;
 | 
						|
	}
 | 
						|
	if (root != client->adapter) {
 | 
						|
		dev_err(&client->dev,
 | 
						|
			"A mctp-i2c-controller client cannot be placed on an I2C mux adapter.\n"
 | 
						|
			" It should be placed on the mux tree root adapter\n"
 | 
						|
			" then set mctp-controller property on adapters to attach\n");
 | 
						|
		rc = -EINVAL;
 | 
						|
		goto err;
 | 
						|
	}
 | 
						|
 | 
						|
	mcli = kzalloc(sizeof(*mcli), GFP_KERNEL);
 | 
						|
	if (!mcli) {
 | 
						|
		rc = -ENOMEM;
 | 
						|
		goto err;
 | 
						|
	}
 | 
						|
	spin_lock_init(&mcli->sel_lock);
 | 
						|
	INIT_LIST_HEAD(&mcli->devs);
 | 
						|
	INIT_LIST_HEAD(&mcli->list);
 | 
						|
	mcli->lladdr = client->addr & 0xff;
 | 
						|
	mcli->client = client;
 | 
						|
	i2c_set_clientdata(client, mcli);
 | 
						|
 | 
						|
	rc = i2c_slave_register(mcli->client, mctp_i2c_slave_cb);
 | 
						|
	if (rc < 0) {
 | 
						|
		dev_err(&client->dev, "i2c register failed %d\n", rc);
 | 
						|
		mcli->client = NULL;
 | 
						|
		i2c_set_clientdata(client, NULL);
 | 
						|
		goto err;
 | 
						|
	}
 | 
						|
 | 
						|
	return mcli;
 | 
						|
err:
 | 
						|
	if (mcli) {
 | 
						|
		i2c_unregister_device(mcli->client);
 | 
						|
		kfree(mcli);
 | 
						|
	}
 | 
						|
	return ERR_PTR(rc);
 | 
						|
}
 | 
						|
 | 
						|
static void mctp_i2c_free_client(struct mctp_i2c_client *mcli)
 | 
						|
{
 | 
						|
	int rc;
 | 
						|
 | 
						|
	WARN_ON(!mutex_is_locked(&driver_clients_lock));
 | 
						|
	WARN_ON(!list_empty(&mcli->devs));
 | 
						|
	WARN_ON(mcli->sel); /* sanity check, no locking */
 | 
						|
 | 
						|
	rc = i2c_slave_unregister(mcli->client);
 | 
						|
	/* Leak if it fails, we can't propagate errors upwards */
 | 
						|
	if (rc < 0)
 | 
						|
		dev_err(&mcli->client->dev, "i2c unregister failed %d\n", rc);
 | 
						|
	else
 | 
						|
		kfree(mcli);
 | 
						|
}
 | 
						|
 | 
						|
/* Switch the mctp i2c device to receive responses.
 | 
						|
 * Call with sel_lock held
 | 
						|
 */
 | 
						|
static void __mctp_i2c_device_select(struct mctp_i2c_client *mcli,
 | 
						|
				     struct mctp_i2c_dev *midev)
 | 
						|
{
 | 
						|
	assert_spin_locked(&mcli->sel_lock);
 | 
						|
	if (midev)
 | 
						|
		dev_hold(midev->ndev);
 | 
						|
	if (mcli->sel)
 | 
						|
		dev_put(mcli->sel->ndev);
 | 
						|
	mcli->sel = midev;
 | 
						|
}
 | 
						|
 | 
						|
/* Switch the mctp i2c device to receive responses */
 | 
						|
static void mctp_i2c_device_select(struct mctp_i2c_client *mcli,
 | 
						|
				   struct mctp_i2c_dev *midev)
 | 
						|
{
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	spin_lock_irqsave(&mcli->sel_lock, flags);
 | 
						|
	__mctp_i2c_device_select(mcli, midev);
 | 
						|
	spin_unlock_irqrestore(&mcli->sel_lock, flags);
 | 
						|
}
 | 
						|
 | 
						|
static int mctp_i2c_slave_cb(struct i2c_client *client,
 | 
						|
			     enum i2c_slave_event event, u8 *val)
 | 
						|
{
 | 
						|
	struct mctp_i2c_client *mcli = i2c_get_clientdata(client);
 | 
						|
	struct mctp_i2c_dev *midev = NULL;
 | 
						|
	unsigned long flags;
 | 
						|
	int rc = 0;
 | 
						|
 | 
						|
	spin_lock_irqsave(&mcli->sel_lock, flags);
 | 
						|
	midev = mcli->sel;
 | 
						|
	if (midev)
 | 
						|
		dev_hold(midev->ndev);
 | 
						|
	spin_unlock_irqrestore(&mcli->sel_lock, flags);
 | 
						|
 | 
						|
	if (!midev)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	switch (event) {
 | 
						|
	case I2C_SLAVE_WRITE_RECEIVED:
 | 
						|
		if (midev->rx_pos < MCTP_I2C_BUFSZ) {
 | 
						|
			midev->rx_buffer[midev->rx_pos] = *val;
 | 
						|
			midev->rx_pos++;
 | 
						|
		} else {
 | 
						|
			midev->ndev->stats.rx_over_errors++;
 | 
						|
		}
 | 
						|
 | 
						|
		break;
 | 
						|
	case I2C_SLAVE_WRITE_REQUESTED:
 | 
						|
		/* dest_slave as first byte */
 | 
						|
		midev->rx_buffer[0] = mcli->lladdr << 1;
 | 
						|
		midev->rx_pos = 1;
 | 
						|
		break;
 | 
						|
	case I2C_SLAVE_STOP:
 | 
						|
		rc = mctp_i2c_recv(midev);
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	dev_put(midev->ndev);
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
 | 
						|
/* Processes incoming data that has been accumulated by the slave cb */
 | 
						|
static int mctp_i2c_recv(struct mctp_i2c_dev *midev)
 | 
						|
{
 | 
						|
	struct net_device *ndev = midev->ndev;
 | 
						|
	struct mctp_i2c_hdr *hdr;
 | 
						|
	struct mctp_skb_cb *cb;
 | 
						|
	struct sk_buff *skb;
 | 
						|
	unsigned long flags;
 | 
						|
	u8 pec, calc_pec;
 | 
						|
	size_t recvlen;
 | 
						|
	int status;
 | 
						|
 | 
						|
	/* + 1 for the PEC */
 | 
						|
	if (midev->rx_pos < MCTP_I2C_MINLEN + 1) {
 | 
						|
		ndev->stats.rx_length_errors++;
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
	/* recvlen excludes PEC */
 | 
						|
	recvlen = midev->rx_pos - 1;
 | 
						|
 | 
						|
	hdr = (void *)midev->rx_buffer;
 | 
						|
	if (hdr->command != MCTP_I2C_COMMANDCODE) {
 | 
						|
		ndev->stats.rx_dropped++;
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	if (hdr->byte_count + offsetof(struct mctp_i2c_hdr, source_slave) != recvlen) {
 | 
						|
		ndev->stats.rx_length_errors++;
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	pec = midev->rx_buffer[midev->rx_pos - 1];
 | 
						|
	calc_pec = i2c_smbus_pec(0, midev->rx_buffer, recvlen);
 | 
						|
	if (pec != calc_pec) {
 | 
						|
		ndev->stats.rx_crc_errors++;
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	skb = netdev_alloc_skb(ndev, recvlen);
 | 
						|
	if (!skb) {
 | 
						|
		ndev->stats.rx_dropped++;
 | 
						|
		return -ENOMEM;
 | 
						|
	}
 | 
						|
 | 
						|
	skb->protocol = htons(ETH_P_MCTP);
 | 
						|
	skb_put_data(skb, midev->rx_buffer, recvlen);
 | 
						|
	skb_reset_mac_header(skb);
 | 
						|
	skb_pull(skb, sizeof(struct mctp_i2c_hdr));
 | 
						|
	skb_reset_network_header(skb);
 | 
						|
 | 
						|
	cb = __mctp_cb(skb);
 | 
						|
	cb->halen = 1;
 | 
						|
	cb->haddr[0] = hdr->source_slave >> 1;
 | 
						|
 | 
						|
	/* We need to ensure that the netif is not used once netdev
 | 
						|
	 * unregister occurs
 | 
						|
	 */
 | 
						|
	spin_lock_irqsave(&midev->lock, flags);
 | 
						|
	if (midev->allow_rx) {
 | 
						|
		reinit_completion(&midev->rx_done);
 | 
						|
		spin_unlock_irqrestore(&midev->lock, flags);
 | 
						|
 | 
						|
		status = netif_rx(skb);
 | 
						|
		complete(&midev->rx_done);
 | 
						|
	} else {
 | 
						|
		status = NET_RX_DROP;
 | 
						|
		spin_unlock_irqrestore(&midev->lock, flags);
 | 
						|
	}
 | 
						|
 | 
						|
	if (status == NET_RX_SUCCESS) {
 | 
						|
		ndev->stats.rx_packets++;
 | 
						|
		ndev->stats.rx_bytes += recvlen;
 | 
						|
	} else {
 | 
						|
		ndev->stats.rx_dropped++;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
enum mctp_i2c_flow_state {
 | 
						|
	MCTP_I2C_TX_FLOW_INVALID,
 | 
						|
	MCTP_I2C_TX_FLOW_NONE,
 | 
						|
	MCTP_I2C_TX_FLOW_NEW,
 | 
						|
	MCTP_I2C_TX_FLOW_EXISTING,
 | 
						|
};
 | 
						|
 | 
						|
static enum mctp_i2c_flow_state
 | 
						|
mctp_i2c_get_tx_flow_state(struct mctp_i2c_dev *midev, struct sk_buff *skb)
 | 
						|
{
 | 
						|
	enum mctp_i2c_flow_state state;
 | 
						|
	struct mctp_sk_key *key;
 | 
						|
	struct mctp_flow *flow;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	flow = skb_ext_find(skb, SKB_EXT_MCTP);
 | 
						|
	if (!flow)
 | 
						|
		return MCTP_I2C_TX_FLOW_NONE;
 | 
						|
 | 
						|
	key = flow->key;
 | 
						|
	if (!key)
 | 
						|
		return MCTP_I2C_TX_FLOW_NONE;
 | 
						|
 | 
						|
	spin_lock_irqsave(&key->lock, flags);
 | 
						|
	/* If the key is present but invalid, we're unlikely to be able
 | 
						|
	 * to handle the flow at all; just drop now
 | 
						|
	 */
 | 
						|
	if (!key->valid) {
 | 
						|
		state = MCTP_I2C_TX_FLOW_INVALID;
 | 
						|
	} else {
 | 
						|
		switch (key->dev_flow_state) {
 | 
						|
		case MCTP_I2C_FLOW_STATE_NEW:
 | 
						|
			key->dev_flow_state = MCTP_I2C_FLOW_STATE_ACTIVE;
 | 
						|
			state = MCTP_I2C_TX_FLOW_NEW;
 | 
						|
			break;
 | 
						|
		case MCTP_I2C_FLOW_STATE_ACTIVE:
 | 
						|
			state = MCTP_I2C_TX_FLOW_EXISTING;
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			state = MCTP_I2C_TX_FLOW_INVALID;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	spin_unlock_irqrestore(&key->lock, flags);
 | 
						|
 | 
						|
	return state;
 | 
						|
}
 | 
						|
 | 
						|
/* We're not contending with ourselves here; we only need to exclude other
 | 
						|
 * i2c clients from using the bus. refcounts are simply to prevent
 | 
						|
 * recursive locking.
 | 
						|
 */
 | 
						|
static void mctp_i2c_lock_nest(struct mctp_i2c_dev *midev)
 | 
						|
{
 | 
						|
	unsigned long flags;
 | 
						|
	bool lock;
 | 
						|
 | 
						|
	spin_lock_irqsave(&midev->lock, flags);
 | 
						|
	lock = midev->i2c_lock_count == 0;
 | 
						|
	midev->i2c_lock_count++;
 | 
						|
	spin_unlock_irqrestore(&midev->lock, flags);
 | 
						|
 | 
						|
	if (lock)
 | 
						|
		i2c_lock_bus(midev->adapter, I2C_LOCK_SEGMENT);
 | 
						|
}
 | 
						|
 | 
						|
static void mctp_i2c_unlock_nest(struct mctp_i2c_dev *midev)
 | 
						|
{
 | 
						|
	unsigned long flags;
 | 
						|
	bool unlock;
 | 
						|
 | 
						|
	spin_lock_irqsave(&midev->lock, flags);
 | 
						|
	if (!WARN_ONCE(midev->i2c_lock_count == 0, "lock count underflow!"))
 | 
						|
		midev->i2c_lock_count--;
 | 
						|
	unlock = midev->i2c_lock_count == 0;
 | 
						|
	spin_unlock_irqrestore(&midev->lock, flags);
 | 
						|
 | 
						|
	if (unlock)
 | 
						|
		i2c_unlock_bus(midev->adapter, I2C_LOCK_SEGMENT);
 | 
						|
}
 | 
						|
 | 
						|
/* Unlocks the bus if was previously locked, used for cleanup */
 | 
						|
static void mctp_i2c_unlock_reset(struct mctp_i2c_dev *midev)
 | 
						|
{
 | 
						|
	unsigned long flags;
 | 
						|
	bool unlock;
 | 
						|
 | 
						|
	spin_lock_irqsave(&midev->lock, flags);
 | 
						|
	unlock = midev->i2c_lock_count > 0;
 | 
						|
	midev->i2c_lock_count = 0;
 | 
						|
	spin_unlock_irqrestore(&midev->lock, flags);
 | 
						|
 | 
						|
	if (unlock)
 | 
						|
		i2c_unlock_bus(midev->adapter, I2C_LOCK_SEGMENT);
 | 
						|
}
 | 
						|
 | 
						|
static void mctp_i2c_invalidate_tx_flow(struct mctp_i2c_dev *midev,
 | 
						|
					struct sk_buff *skb)
 | 
						|
{
 | 
						|
	struct mctp_sk_key *key;
 | 
						|
	struct mctp_flow *flow;
 | 
						|
	unsigned long flags;
 | 
						|
	bool release;
 | 
						|
 | 
						|
	flow = skb_ext_find(skb, SKB_EXT_MCTP);
 | 
						|
	if (!flow)
 | 
						|
		return;
 | 
						|
 | 
						|
	key = flow->key;
 | 
						|
	if (!key)
 | 
						|
		return;
 | 
						|
 | 
						|
	spin_lock_irqsave(&key->lock, flags);
 | 
						|
	if (key->manual_alloc) {
 | 
						|
		/* we don't have control over lifetimes for manually-allocated
 | 
						|
		 * keys, so cannot assume we can invalidate all future flows
 | 
						|
		 * that would use this key.
 | 
						|
		 */
 | 
						|
		release = false;
 | 
						|
	} else {
 | 
						|
		release = key->dev_flow_state == MCTP_I2C_FLOW_STATE_ACTIVE;
 | 
						|
		key->dev_flow_state = MCTP_I2C_FLOW_STATE_INVALID;
 | 
						|
	}
 | 
						|
	spin_unlock_irqrestore(&key->lock, flags);
 | 
						|
 | 
						|
	/* if we have changed state from active, the flow held a reference on
 | 
						|
	 * the lock; release that now.
 | 
						|
	 */
 | 
						|
	if (release)
 | 
						|
		mctp_i2c_unlock_nest(midev);
 | 
						|
}
 | 
						|
 | 
						|
static void mctp_i2c_xmit(struct mctp_i2c_dev *midev, struct sk_buff *skb)
 | 
						|
{
 | 
						|
	struct net_device_stats *stats = &midev->ndev->stats;
 | 
						|
	enum mctp_i2c_flow_state fs;
 | 
						|
	struct mctp_i2c_hdr *hdr;
 | 
						|
	struct i2c_msg msg = {0};
 | 
						|
	u8 *pecp;
 | 
						|
	int rc;
 | 
						|
 | 
						|
	fs = mctp_i2c_get_tx_flow_state(midev, skb);
 | 
						|
 | 
						|
	hdr = (void *)skb_mac_header(skb);
 | 
						|
	/* Sanity check that packet contents matches skb length,
 | 
						|
	 * and can't exceed MCTP_I2C_BUFSZ
 | 
						|
	 */
 | 
						|
	if (skb->len != hdr->byte_count + 3) {
 | 
						|
		dev_warn_ratelimited(&midev->adapter->dev,
 | 
						|
				     "Bad tx length %d vs skb %u\n",
 | 
						|
				     hdr->byte_count + 3, skb->len);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if (skb_tailroom(skb) >= 1) {
 | 
						|
		/* Linear case with space, we can just append the PEC */
 | 
						|
		skb_put(skb, 1);
 | 
						|
	} else {
 | 
						|
		/* Otherwise need to copy the buffer */
 | 
						|
		skb_copy_bits(skb, 0, midev->tx_scratch, skb->len);
 | 
						|
		hdr = (void *)midev->tx_scratch;
 | 
						|
	}
 | 
						|
 | 
						|
	pecp = (void *)&hdr->source_slave + hdr->byte_count;
 | 
						|
	*pecp = i2c_smbus_pec(0, (u8 *)hdr, hdr->byte_count + 3);
 | 
						|
	msg.buf = (void *)&hdr->command;
 | 
						|
	/* command, bytecount, data, pec */
 | 
						|
	msg.len = 2 + hdr->byte_count + 1;
 | 
						|
	msg.addr = hdr->dest_slave >> 1;
 | 
						|
 | 
						|
	switch (fs) {
 | 
						|
	case MCTP_I2C_TX_FLOW_NONE:
 | 
						|
		/* no flow: full lock & unlock */
 | 
						|
		mctp_i2c_lock_nest(midev);
 | 
						|
		mctp_i2c_device_select(midev->client, midev);
 | 
						|
		rc = __i2c_transfer(midev->adapter, &msg, 1);
 | 
						|
		mctp_i2c_unlock_nest(midev);
 | 
						|
		break;
 | 
						|
 | 
						|
	case MCTP_I2C_TX_FLOW_NEW:
 | 
						|
		/* new flow: lock, tx, but don't unlock; that will happen
 | 
						|
		 * on flow release
 | 
						|
		 */
 | 
						|
		mctp_i2c_lock_nest(midev);
 | 
						|
		mctp_i2c_device_select(midev->client, midev);
 | 
						|
		fallthrough;
 | 
						|
 | 
						|
	case MCTP_I2C_TX_FLOW_EXISTING:
 | 
						|
		/* existing flow: we already have the lock; just tx */
 | 
						|
		rc = __i2c_transfer(midev->adapter, &msg, 1);
 | 
						|
 | 
						|
		/* on tx errors, the flow can no longer be considered valid */
 | 
						|
		if (rc < 0)
 | 
						|
			mctp_i2c_invalidate_tx_flow(midev, skb);
 | 
						|
 | 
						|
		break;
 | 
						|
 | 
						|
	case MCTP_I2C_TX_FLOW_INVALID:
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if (rc < 0) {
 | 
						|
		dev_warn_ratelimited(&midev->adapter->dev,
 | 
						|
				     "__i2c_transfer failed %d\n", rc);
 | 
						|
		stats->tx_errors++;
 | 
						|
	} else {
 | 
						|
		stats->tx_bytes += skb->len;
 | 
						|
		stats->tx_packets++;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void mctp_i2c_flow_release(struct mctp_i2c_dev *midev)
 | 
						|
{
 | 
						|
	unsigned long flags;
 | 
						|
	bool unlock;
 | 
						|
 | 
						|
	spin_lock_irqsave(&midev->lock, flags);
 | 
						|
	if (midev->release_count > midev->i2c_lock_count) {
 | 
						|
		WARN_ONCE(1, "release count overflow");
 | 
						|
		midev->release_count = midev->i2c_lock_count;
 | 
						|
	}
 | 
						|
 | 
						|
	midev->i2c_lock_count -= midev->release_count;
 | 
						|
	unlock = midev->i2c_lock_count == 0 && midev->release_count > 0;
 | 
						|
	midev->release_count = 0;
 | 
						|
	spin_unlock_irqrestore(&midev->lock, flags);
 | 
						|
 | 
						|
	if (unlock)
 | 
						|
		i2c_unlock_bus(midev->adapter, I2C_LOCK_SEGMENT);
 | 
						|
}
 | 
						|
 | 
						|
static int mctp_i2c_header_create(struct sk_buff *skb, struct net_device *dev,
 | 
						|
				  unsigned short type, const void *daddr,
 | 
						|
	   const void *saddr, unsigned int len)
 | 
						|
{
 | 
						|
	struct mctp_i2c_hdr *hdr;
 | 
						|
	struct mctp_hdr *mhdr;
 | 
						|
	u8 lldst, llsrc;
 | 
						|
	int rc;
 | 
						|
 | 
						|
	if (len > MCTP_I2C_MAXMTU)
 | 
						|
		return -EMSGSIZE;
 | 
						|
 | 
						|
	if (!daddr || !saddr)
 | 
						|
		return -EINVAL;
 | 
						|
 | 
						|
	lldst = *((u8 *)daddr);
 | 
						|
	llsrc = *((u8 *)saddr);
 | 
						|
 | 
						|
	rc = skb_cow_head(skb, sizeof(struct mctp_i2c_hdr));
 | 
						|
	if (rc)
 | 
						|
		return rc;
 | 
						|
 | 
						|
	skb_push(skb, sizeof(struct mctp_i2c_hdr));
 | 
						|
	skb_reset_mac_header(skb);
 | 
						|
	hdr = (void *)skb_mac_header(skb);
 | 
						|
	mhdr = mctp_hdr(skb);
 | 
						|
	hdr->dest_slave = (lldst << 1) & 0xff;
 | 
						|
	hdr->command = MCTP_I2C_COMMANDCODE;
 | 
						|
	hdr->byte_count = len + 1;
 | 
						|
	hdr->source_slave = ((llsrc << 1) & 0xff) | 0x01;
 | 
						|
	mhdr->ver = 0x01;
 | 
						|
 | 
						|
	return sizeof(struct mctp_i2c_hdr);
 | 
						|
}
 | 
						|
 | 
						|
static int mctp_i2c_tx_thread(void *data)
 | 
						|
{
 | 
						|
	struct mctp_i2c_dev *midev = data;
 | 
						|
	struct sk_buff *skb;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	for (;;) {
 | 
						|
		if (kthread_should_stop())
 | 
						|
			break;
 | 
						|
 | 
						|
		spin_lock_irqsave(&midev->tx_queue.lock, flags);
 | 
						|
		skb = __skb_dequeue(&midev->tx_queue);
 | 
						|
		if (netif_queue_stopped(midev->ndev))
 | 
						|
			netif_wake_queue(midev->ndev);
 | 
						|
		spin_unlock_irqrestore(&midev->tx_queue.lock, flags);
 | 
						|
 | 
						|
		if (skb == &midev->unlock_marker) {
 | 
						|
			mctp_i2c_flow_release(midev);
 | 
						|
 | 
						|
		} else if (skb) {
 | 
						|
			mctp_i2c_xmit(midev, skb);
 | 
						|
			kfree_skb(skb);
 | 
						|
 | 
						|
		} else {
 | 
						|
			wait_event_idle(midev->tx_wq,
 | 
						|
					!skb_queue_empty(&midev->tx_queue) ||
 | 
						|
				   kthread_should_stop());
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static netdev_tx_t mctp_i2c_start_xmit(struct sk_buff *skb,
 | 
						|
				       struct net_device *dev)
 | 
						|
{
 | 
						|
	struct mctp_i2c_dev *midev = netdev_priv(dev);
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	spin_lock_irqsave(&midev->tx_queue.lock, flags);
 | 
						|
	if (skb_queue_len(&midev->tx_queue) >= MCTP_I2C_TX_WORK_LEN) {
 | 
						|
		netif_stop_queue(dev);
 | 
						|
		spin_unlock_irqrestore(&midev->tx_queue.lock, flags);
 | 
						|
		netdev_err(dev, "BUG! Tx Ring full when queue awake!\n");
 | 
						|
		return NETDEV_TX_BUSY;
 | 
						|
	}
 | 
						|
 | 
						|
	__skb_queue_tail(&midev->tx_queue, skb);
 | 
						|
	if (skb_queue_len(&midev->tx_queue) == MCTP_I2C_TX_WORK_LEN)
 | 
						|
		netif_stop_queue(dev);
 | 
						|
	spin_unlock_irqrestore(&midev->tx_queue.lock, flags);
 | 
						|
 | 
						|
	wake_up(&midev->tx_wq);
 | 
						|
	return NETDEV_TX_OK;
 | 
						|
}
 | 
						|
 | 
						|
static void mctp_i2c_release_flow(struct mctp_dev *mdev,
 | 
						|
				  struct mctp_sk_key *key)
 | 
						|
 | 
						|
{
 | 
						|
	struct mctp_i2c_dev *midev = netdev_priv(mdev->dev);
 | 
						|
	bool queue_release = false;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	spin_lock_irqsave(&midev->lock, flags);
 | 
						|
	/* if we have seen the flow/key previously, we need to pair the
 | 
						|
	 * original lock with a release
 | 
						|
	 */
 | 
						|
	if (key->dev_flow_state == MCTP_I2C_FLOW_STATE_ACTIVE) {
 | 
						|
		midev->release_count++;
 | 
						|
		queue_release = true;
 | 
						|
	}
 | 
						|
	key->dev_flow_state = MCTP_I2C_FLOW_STATE_INVALID;
 | 
						|
	spin_unlock_irqrestore(&midev->lock, flags);
 | 
						|
 | 
						|
	if (queue_release) {
 | 
						|
		/* Ensure we have a release operation queued, through the fake
 | 
						|
		 * marker skb
 | 
						|
		 */
 | 
						|
		spin_lock(&midev->tx_queue.lock);
 | 
						|
		if (!midev->unlock_marker.next)
 | 
						|
			__skb_queue_tail(&midev->tx_queue,
 | 
						|
					 &midev->unlock_marker);
 | 
						|
		spin_unlock(&midev->tx_queue.lock);
 | 
						|
		wake_up(&midev->tx_wq);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static const struct net_device_ops mctp_i2c_ops = {
 | 
						|
	.ndo_start_xmit = mctp_i2c_start_xmit,
 | 
						|
	.ndo_uninit = mctp_i2c_ndo_uninit,
 | 
						|
	.ndo_open = mctp_i2c_ndo_open,
 | 
						|
};
 | 
						|
 | 
						|
static const struct header_ops mctp_i2c_headops = {
 | 
						|
	.create = mctp_i2c_header_create,
 | 
						|
};
 | 
						|
 | 
						|
static const struct mctp_netdev_ops mctp_i2c_mctp_ops = {
 | 
						|
	.release_flow = mctp_i2c_release_flow,
 | 
						|
};
 | 
						|
 | 
						|
static void mctp_i2c_net_setup(struct net_device *dev)
 | 
						|
{
 | 
						|
	dev->type = ARPHRD_MCTP;
 | 
						|
 | 
						|
	dev->mtu = MCTP_I2C_MAXMTU;
 | 
						|
	dev->min_mtu = MCTP_I2C_MINMTU;
 | 
						|
	dev->max_mtu = MCTP_I2C_MAXMTU;
 | 
						|
	dev->tx_queue_len = MCTP_I2C_TX_QUEUE_LEN;
 | 
						|
 | 
						|
	dev->hard_header_len = sizeof(struct mctp_i2c_hdr);
 | 
						|
	dev->addr_len = 1;
 | 
						|
 | 
						|
	dev->netdev_ops		= &mctp_i2c_ops;
 | 
						|
	dev->header_ops		= &mctp_i2c_headops;
 | 
						|
}
 | 
						|
 | 
						|
/* Populates the mctp_i2c_dev priv struct for a netdev.
 | 
						|
 * Returns an error pointer on failure.
 | 
						|
 */
 | 
						|
static struct mctp_i2c_dev *mctp_i2c_midev_init(struct net_device *dev,
 | 
						|
						struct mctp_i2c_client *mcli,
 | 
						|
						struct i2c_adapter *adap)
 | 
						|
{
 | 
						|
	struct mctp_i2c_dev *midev = netdev_priv(dev);
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	midev->tx_thread = kthread_create(mctp_i2c_tx_thread, midev,
 | 
						|
					  "%s/tx", dev->name);
 | 
						|
	if (IS_ERR(midev->tx_thread))
 | 
						|
		return ERR_CAST(midev->tx_thread);
 | 
						|
 | 
						|
	midev->ndev = dev;
 | 
						|
	get_device(&adap->dev);
 | 
						|
	midev->adapter = adap;
 | 
						|
	get_device(&mcli->client->dev);
 | 
						|
	midev->client = mcli;
 | 
						|
	INIT_LIST_HEAD(&midev->list);
 | 
						|
	spin_lock_init(&midev->lock);
 | 
						|
	midev->i2c_lock_count = 0;
 | 
						|
	midev->release_count = 0;
 | 
						|
	init_completion(&midev->rx_done);
 | 
						|
	complete(&midev->rx_done);
 | 
						|
	init_waitqueue_head(&midev->tx_wq);
 | 
						|
	skb_queue_head_init(&midev->tx_queue);
 | 
						|
 | 
						|
	/* Add to the parent mcli */
 | 
						|
	spin_lock_irqsave(&mcli->sel_lock, flags);
 | 
						|
	list_add(&midev->list, &mcli->devs);
 | 
						|
	/* Select a device by default */
 | 
						|
	if (!mcli->sel)
 | 
						|
		__mctp_i2c_device_select(mcli, midev);
 | 
						|
	spin_unlock_irqrestore(&mcli->sel_lock, flags);
 | 
						|
 | 
						|
	/* Start the worker thread */
 | 
						|
	wake_up_process(midev->tx_thread);
 | 
						|
 | 
						|
	return midev;
 | 
						|
}
 | 
						|
 | 
						|
/* Counterpart of mctp_i2c_midev_init */
 | 
						|
static void mctp_i2c_midev_free(struct mctp_i2c_dev *midev)
 | 
						|
{
 | 
						|
	struct mctp_i2c_client *mcli = midev->client;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	if (midev->tx_thread) {
 | 
						|
		kthread_stop(midev->tx_thread);
 | 
						|
		midev->tx_thread = NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Unconditionally unlock on close */
 | 
						|
	mctp_i2c_unlock_reset(midev);
 | 
						|
 | 
						|
	/* Remove the netdev from the parent i2c client. */
 | 
						|
	spin_lock_irqsave(&mcli->sel_lock, flags);
 | 
						|
	list_del(&midev->list);
 | 
						|
	if (mcli->sel == midev) {
 | 
						|
		struct mctp_i2c_dev *first;
 | 
						|
 | 
						|
		first = list_first_entry_or_null(&mcli->devs, struct mctp_i2c_dev, list);
 | 
						|
		__mctp_i2c_device_select(mcli, first);
 | 
						|
	}
 | 
						|
	spin_unlock_irqrestore(&mcli->sel_lock, flags);
 | 
						|
 | 
						|
	skb_queue_purge(&midev->tx_queue);
 | 
						|
	put_device(&midev->adapter->dev);
 | 
						|
	put_device(&mcli->client->dev);
 | 
						|
}
 | 
						|
 | 
						|
/* Stops, unregisters, and frees midev */
 | 
						|
static void mctp_i2c_unregister(struct mctp_i2c_dev *midev)
 | 
						|
{
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	/* Stop tx thread prior to unregister, it uses netif_() functions */
 | 
						|
	kthread_stop(midev->tx_thread);
 | 
						|
	midev->tx_thread = NULL;
 | 
						|
 | 
						|
	/* Prevent any new rx in mctp_i2c_recv(), let any pending work finish */
 | 
						|
	spin_lock_irqsave(&midev->lock, flags);
 | 
						|
	midev->allow_rx = false;
 | 
						|
	spin_unlock_irqrestore(&midev->lock, flags);
 | 
						|
	wait_for_completion(&midev->rx_done);
 | 
						|
 | 
						|
	mctp_unregister_netdev(midev->ndev);
 | 
						|
	/* midev has been freed now by mctp_i2c_ndo_uninit callback */
 | 
						|
 | 
						|
	free_netdev(midev->ndev);
 | 
						|
}
 | 
						|
 | 
						|
static void mctp_i2c_ndo_uninit(struct net_device *dev)
 | 
						|
{
 | 
						|
	struct mctp_i2c_dev *midev = netdev_priv(dev);
 | 
						|
 | 
						|
	/* Perform cleanup here to ensure that mcli->sel isn't holding
 | 
						|
	 * a reference that would prevent unregister_netdevice()
 | 
						|
	 * from completing.
 | 
						|
	 */
 | 
						|
	mctp_i2c_midev_free(midev);
 | 
						|
}
 | 
						|
 | 
						|
static int mctp_i2c_ndo_open(struct net_device *dev)
 | 
						|
{
 | 
						|
	struct mctp_i2c_dev *midev = netdev_priv(dev);
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	/* i2c rx handler can only pass packets once the netdev is registered */
 | 
						|
	spin_lock_irqsave(&midev->lock, flags);
 | 
						|
	midev->allow_rx = true;
 | 
						|
	spin_unlock_irqrestore(&midev->lock, flags);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int mctp_i2c_add_netdev(struct mctp_i2c_client *mcli,
 | 
						|
			       struct i2c_adapter *adap)
 | 
						|
{
 | 
						|
	struct mctp_i2c_dev *midev = NULL;
 | 
						|
	struct net_device *ndev = NULL;
 | 
						|
	struct i2c_adapter *root;
 | 
						|
	unsigned long flags;
 | 
						|
	char namebuf[30];
 | 
						|
	int rc;
 | 
						|
 | 
						|
	root = mux_root_adapter(adap);
 | 
						|
	if (root != mcli->client->adapter) {
 | 
						|
		dev_err(&mcli->client->dev,
 | 
						|
			"I2C adapter %s is not a child bus of %s\n",
 | 
						|
			mcli->client->adapter->name, root->name);
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	WARN_ON(!mutex_is_locked(&driver_clients_lock));
 | 
						|
	snprintf(namebuf, sizeof(namebuf), "mctpi2c%d", adap->nr);
 | 
						|
	ndev = alloc_netdev(sizeof(*midev), namebuf, NET_NAME_ENUM, mctp_i2c_net_setup);
 | 
						|
	if (!ndev) {
 | 
						|
		dev_err(&mcli->client->dev, "alloc netdev failed\n");
 | 
						|
		rc = -ENOMEM;
 | 
						|
		goto err;
 | 
						|
	}
 | 
						|
	dev_net_set(ndev, current->nsproxy->net_ns);
 | 
						|
	SET_NETDEV_DEV(ndev, &adap->dev);
 | 
						|
	dev_addr_set(ndev, &mcli->lladdr);
 | 
						|
 | 
						|
	midev = mctp_i2c_midev_init(ndev, mcli, adap);
 | 
						|
	if (IS_ERR(midev)) {
 | 
						|
		rc = PTR_ERR(midev);
 | 
						|
		midev = NULL;
 | 
						|
		goto err;
 | 
						|
	}
 | 
						|
 | 
						|
	rc = mctp_register_netdev(ndev, &mctp_i2c_mctp_ops,
 | 
						|
				  MCTP_PHYS_BINDING_SMBUS);
 | 
						|
	if (rc < 0) {
 | 
						|
		dev_err(&mcli->client->dev,
 | 
						|
			"register netdev \"%s\" failed %d\n",
 | 
						|
			ndev->name, rc);
 | 
						|
		goto err;
 | 
						|
	}
 | 
						|
 | 
						|
	spin_lock_irqsave(&midev->lock, flags);
 | 
						|
	midev->allow_rx = false;
 | 
						|
	spin_unlock_irqrestore(&midev->lock, flags);
 | 
						|
 | 
						|
	return 0;
 | 
						|
err:
 | 
						|
	if (midev)
 | 
						|
		mctp_i2c_midev_free(midev);
 | 
						|
	if (ndev)
 | 
						|
		free_netdev(ndev);
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
 | 
						|
/* Removes any netdev for adap. mcli is the parent root i2c client */
 | 
						|
static void mctp_i2c_remove_netdev(struct mctp_i2c_client *mcli,
 | 
						|
				   struct i2c_adapter *adap)
 | 
						|
{
 | 
						|
	struct mctp_i2c_dev *midev = NULL, *m = NULL;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	WARN_ON(!mutex_is_locked(&driver_clients_lock));
 | 
						|
	spin_lock_irqsave(&mcli->sel_lock, flags);
 | 
						|
	/* List size is limited by number of MCTP netdevs on a single hardware bus */
 | 
						|
	list_for_each_entry(m, &mcli->devs, list)
 | 
						|
		if (m->adapter == adap) {
 | 
						|
			midev = m;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	spin_unlock_irqrestore(&mcli->sel_lock, flags);
 | 
						|
 | 
						|
	if (midev)
 | 
						|
		mctp_i2c_unregister(midev);
 | 
						|
}
 | 
						|
 | 
						|
/* Determines whether a device is an i2c adapter.
 | 
						|
 * Optionally returns the root i2c_adapter
 | 
						|
 */
 | 
						|
static struct i2c_adapter *mctp_i2c_get_adapter(struct device *dev,
 | 
						|
						struct i2c_adapter **ret_root)
 | 
						|
{
 | 
						|
	struct i2c_adapter *root, *adap;
 | 
						|
 | 
						|
	if (dev->type != &i2c_adapter_type)
 | 
						|
		return NULL;
 | 
						|
	adap = to_i2c_adapter(dev);
 | 
						|
	root = mux_root_adapter(adap);
 | 
						|
	WARN_ONCE(!root, "MCTP I2C failed to find root adapter for %s\n",
 | 
						|
		  dev_name(dev));
 | 
						|
	if (!root)
 | 
						|
		return NULL;
 | 
						|
	if (ret_root)
 | 
						|
		*ret_root = root;
 | 
						|
	return adap;
 | 
						|
}
 | 
						|
 | 
						|
/* Determines whether a device is an i2c adapter with the "mctp-controller"
 | 
						|
 * devicetree property set. If adap is not an OF node, returns match_no_of
 | 
						|
 */
 | 
						|
static bool mctp_i2c_adapter_match(struct i2c_adapter *adap, bool match_no_of)
 | 
						|
{
 | 
						|
	if (!adap->dev.of_node)
 | 
						|
		return match_no_of;
 | 
						|
	return of_property_read_bool(adap->dev.of_node, MCTP_I2C_OF_PROP);
 | 
						|
}
 | 
						|
 | 
						|
/* Called for each existing i2c device (adapter or client) when a
 | 
						|
 * new mctp-i2c client is probed.
 | 
						|
 */
 | 
						|
static int mctp_i2c_client_try_attach(struct device *dev, void *data)
 | 
						|
{
 | 
						|
	struct i2c_adapter *adap = NULL, *root = NULL;
 | 
						|
	struct mctp_i2c_client *mcli = data;
 | 
						|
 | 
						|
	adap = mctp_i2c_get_adapter(dev, &root);
 | 
						|
	if (!adap)
 | 
						|
		return 0;
 | 
						|
	if (mcli->client->adapter != root)
 | 
						|
		return 0;
 | 
						|
	/* Must either have mctp-controller property on the adapter, or
 | 
						|
	 * be a root adapter if it's non-devicetree
 | 
						|
	 */
 | 
						|
	if (!mctp_i2c_adapter_match(adap, adap == root))
 | 
						|
		return 0;
 | 
						|
 | 
						|
	return mctp_i2c_add_netdev(mcli, adap);
 | 
						|
}
 | 
						|
 | 
						|
static void mctp_i2c_notify_add(struct device *dev)
 | 
						|
{
 | 
						|
	struct mctp_i2c_client *mcli = NULL, *m = NULL;
 | 
						|
	struct i2c_adapter *root = NULL, *adap = NULL;
 | 
						|
	int rc;
 | 
						|
 | 
						|
	adap = mctp_i2c_get_adapter(dev, &root);
 | 
						|
	if (!adap)
 | 
						|
		return;
 | 
						|
	/* Check for mctp-controller property on the adapter */
 | 
						|
	if (!mctp_i2c_adapter_match(adap, false))
 | 
						|
		return;
 | 
						|
 | 
						|
	/* Find an existing mcli for adap's root */
 | 
						|
	mutex_lock(&driver_clients_lock);
 | 
						|
	list_for_each_entry(m, &driver_clients, list) {
 | 
						|
		if (m->client->adapter == root) {
 | 
						|
			mcli = m;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (mcli) {
 | 
						|
		rc = mctp_i2c_add_netdev(mcli, adap);
 | 
						|
		if (rc < 0)
 | 
						|
			dev_warn(dev, "Failed adding mctp-i2c net device\n");
 | 
						|
	}
 | 
						|
	mutex_unlock(&driver_clients_lock);
 | 
						|
}
 | 
						|
 | 
						|
static void mctp_i2c_notify_del(struct device *dev)
 | 
						|
{
 | 
						|
	struct i2c_adapter *root = NULL, *adap = NULL;
 | 
						|
	struct mctp_i2c_client *mcli = NULL;
 | 
						|
 | 
						|
	adap = mctp_i2c_get_adapter(dev, &root);
 | 
						|
	if (!adap)
 | 
						|
		return;
 | 
						|
 | 
						|
	mutex_lock(&driver_clients_lock);
 | 
						|
	list_for_each_entry(mcli, &driver_clients, list) {
 | 
						|
		if (mcli->client->adapter == root) {
 | 
						|
			mctp_i2c_remove_netdev(mcli, adap);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	mutex_unlock(&driver_clients_lock);
 | 
						|
}
 | 
						|
 | 
						|
static int mctp_i2c_probe(struct i2c_client *client)
 | 
						|
{
 | 
						|
	struct mctp_i2c_client *mcli = NULL;
 | 
						|
	int rc;
 | 
						|
 | 
						|
	mutex_lock(&driver_clients_lock);
 | 
						|
	mcli = mctp_i2c_new_client(client);
 | 
						|
	if (IS_ERR(mcli)) {
 | 
						|
		rc = PTR_ERR(mcli);
 | 
						|
		mcli = NULL;
 | 
						|
		goto out;
 | 
						|
	} else {
 | 
						|
		list_add(&mcli->list, &driver_clients);
 | 
						|
	}
 | 
						|
 | 
						|
	/* Add a netdev for adapters that have a 'mctp-controller' property */
 | 
						|
	i2c_for_each_dev(mcli, mctp_i2c_client_try_attach);
 | 
						|
	rc = 0;
 | 
						|
out:
 | 
						|
	mutex_unlock(&driver_clients_lock);
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
 | 
						|
static void mctp_i2c_remove(struct i2c_client *client)
 | 
						|
{
 | 
						|
	struct mctp_i2c_client *mcli = i2c_get_clientdata(client);
 | 
						|
	struct mctp_i2c_dev *midev = NULL, *tmp = NULL;
 | 
						|
 | 
						|
	mutex_lock(&driver_clients_lock);
 | 
						|
	list_del(&mcli->list);
 | 
						|
	/* Remove all child adapter netdevs */
 | 
						|
	list_for_each_entry_safe(midev, tmp, &mcli->devs, list)
 | 
						|
		mctp_i2c_unregister(midev);
 | 
						|
 | 
						|
	mctp_i2c_free_client(mcli);
 | 
						|
	mutex_unlock(&driver_clients_lock);
 | 
						|
}
 | 
						|
 | 
						|
/* We look for a 'mctp-controller' property on I2C busses as they are
 | 
						|
 * added/deleted, creating/removing netdevs as required.
 | 
						|
 */
 | 
						|
static int mctp_i2c_notifier_call(struct notifier_block *nb,
 | 
						|
				  unsigned long action, void *data)
 | 
						|
{
 | 
						|
	struct device *dev = data;
 | 
						|
 | 
						|
	switch (action) {
 | 
						|
	case BUS_NOTIFY_ADD_DEVICE:
 | 
						|
		mctp_i2c_notify_add(dev);
 | 
						|
		break;
 | 
						|
	case BUS_NOTIFY_DEL_DEVICE:
 | 
						|
		mctp_i2c_notify_del(dev);
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	return NOTIFY_DONE;
 | 
						|
}
 | 
						|
 | 
						|
static struct notifier_block mctp_i2c_notifier = {
 | 
						|
	.notifier_call = mctp_i2c_notifier_call,
 | 
						|
};
 | 
						|
 | 
						|
static const struct i2c_device_id mctp_i2c_id[] = {
 | 
						|
	{ "mctp-i2c-interface" },
 | 
						|
	{}
 | 
						|
};
 | 
						|
MODULE_DEVICE_TABLE(i2c, mctp_i2c_id);
 | 
						|
 | 
						|
static const struct of_device_id mctp_i2c_of_match[] = {
 | 
						|
	{ .compatible = "mctp-i2c-controller" },
 | 
						|
	{},
 | 
						|
};
 | 
						|
MODULE_DEVICE_TABLE(of, mctp_i2c_of_match);
 | 
						|
 | 
						|
static struct i2c_driver mctp_i2c_driver = {
 | 
						|
	.driver = {
 | 
						|
		.name = "mctp-i2c-interface",
 | 
						|
		.of_match_table = mctp_i2c_of_match,
 | 
						|
	},
 | 
						|
	.probe = mctp_i2c_probe,
 | 
						|
	.remove = mctp_i2c_remove,
 | 
						|
	.id_table = mctp_i2c_id,
 | 
						|
};
 | 
						|
 | 
						|
static __init int mctp_i2c_mod_init(void)
 | 
						|
{
 | 
						|
	int rc;
 | 
						|
 | 
						|
	pr_info("MCTP I2C interface driver\n");
 | 
						|
	rc = i2c_add_driver(&mctp_i2c_driver);
 | 
						|
	if (rc < 0)
 | 
						|
		return rc;
 | 
						|
	rc = bus_register_notifier(&i2c_bus_type, &mctp_i2c_notifier);
 | 
						|
	if (rc < 0) {
 | 
						|
		i2c_del_driver(&mctp_i2c_driver);
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static __exit void mctp_i2c_mod_exit(void)
 | 
						|
{
 | 
						|
	int rc;
 | 
						|
 | 
						|
	rc = bus_unregister_notifier(&i2c_bus_type, &mctp_i2c_notifier);
 | 
						|
	if (rc < 0)
 | 
						|
		pr_warn("MCTP I2C could not unregister notifier, %d\n", rc);
 | 
						|
	i2c_del_driver(&mctp_i2c_driver);
 | 
						|
}
 | 
						|
 | 
						|
module_init(mctp_i2c_mod_init);
 | 
						|
module_exit(mctp_i2c_mod_exit);
 | 
						|
 | 
						|
MODULE_DESCRIPTION("MCTP I2C device");
 | 
						|
MODULE_LICENSE("GPL v2");
 | 
						|
MODULE_AUTHOR("Matt Johnston <matt@codeconstruct.com.au>");
 |