/* List of all struct mctp_i2c_client *Lockprotectsdriver_clientsandalsopreventsadding/removingadapters *duringmctp_i2c_clientprobe/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 */
/* 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 *muxtree,sharedbymultiplenetdevs
*/ 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;
};
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;
}
/* We need to ensure that the netif is not used once netdev *unregisteroccurs
*/
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);
}
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 *tohandletheflowatall;justdropnow
*/ 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 *i2cclientsfromusingthebus.refcountsaresimplytoprevent *recursivelocking.
*/ staticvoid mctp_i2c_lock_nest(struct mctp_i2c_dev *midev)
{ unsignedlong flags; bool lock;
if (unlock)
i2c_unlock_bus(midev->adapter, I2C_LOCK_SEGMENT);
}
/* Unlocks the bus if was previously locked, used for cleanup */ staticvoid mctp_i2c_unlock_reset(struct mctp_i2c_dev *midev)
{ unsignedlong flags; bool unlock;
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;
}
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 *onflowrelease
*/
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);
spin_lock_irqsave(&midev->lock, flags); /* if we have seen the flow/key previously, we need to pair the *originallockwitharelease
*/ 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 *markerskb
*/
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);
}
}
/* 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);
/* 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 */
/* Perform cleanup here to ensure that mcli->sel isn't holding *areferencethatwouldpreventunregister_netdevice() *fromcompleting.
*/
mctp_i2c_midev_free(midev);
}
/* 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);
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;
}
return0;
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 */ staticvoid mctp_i2c_remove_netdev(struct mctp_i2c_client *mcli, struct i2c_adapter *adap)
{ struct mctp_i2c_dev *midev = NULL, *m = NULL; unsignedlong 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. *Optionallyreturnstherooti2c_adapter
*/ staticstruct 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" *devicetreepropertyset.IfadapisnotanOFnode,returnsmatch_no_of
*/ staticbool 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 *newmctp-i2cclientisprobed.
*/ staticint 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) return0; if (mcli->client->adapter != root) return0; /* Must either have mctp-controller property on the adapter, or *bearootadapterifit'snon-devicetree
*/ if (!mctp_i2c_adapter_match(adap, adap == root)) return0;
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);
}
/* 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;
}
/* We look for a 'mctp-controller' property on I2C busses as they are *added/deleted,creating/removingnetdevsasrequired.
*/ staticint mctp_i2c_notifier_call(struct notifier_block *nb, unsignedlong 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;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.