/* Mandatory Data Byte in an IBI, from DSP0233 */ #define I3C_MDB_MCTP 0xAE /* From MIPI Device Characteristics Register (DCR) Assignments */ #define I3C_DCR_MCTP 0xCC
/* List of mctp_i3c_busdev */ static LIST_HEAD(busdevs); /* Protects busdevs, as well as mctp_i3c_bus.devs lists */ static DEFINE_MUTEX(busdevs_lock);
struct mctp_i3c_bus { struct net_device *ndev;
struct task_struct *tx_thread;
wait_queue_head_t tx_wq; /* tx_lock protects tx_skb and devs */
spinlock_t tx_lock; /* Next skb to transmit */ struct sk_buff *tx_skb; /* Scratch buffer for xmit */
u8 tx_scratch[MCTP_I3C_MAXBUF];
/* Element of busdevs */ struct list_head list;
/* Provisioned ID of our controller */
u64 pid;
struct i3c_bus *bus; /* Head of mctp_i3c_device.list. Protected by busdevs_lock */ struct list_head devs;
};
struct mctp_i3c_device { struct i3c_device *i3c; struct mctp_i3c_bus *mbus; struct list_head list; /* Element of mctp_i3c_bus.devs */
/* Held while tx_thread is using this device */ struct mutex lock;
/* Whether BCR indicates MDB is present in IBI */ bool have_mdb; /* I3C dynamic address */
u8 addr; /* Maximum read length */
u16 mrl; /* Maximum write length */
u16 mwl; /* Provisioned ID */
u64 pid;
};
/* We synthesise a mac header using the Provisioned ID. *Usedtopassdesttomctp_i3c_start_xmit.
*/ struct mctp_i3c_internal_hdr {
u8 dest[PID_SIZE];
u8 source[PID_SIZE];
} __packed;
skb->protocol = htons(ETH_P_MCTP); /* Create a header for internal use */
skb_reset_mac_header(skb);
ihdr = skb_put(skb, sizeof(struct mctp_i3c_internal_hdr));
put_unaligned_be48(mi->pid, ihdr->source);
put_unaligned_be48(mi->mbus->pid, ihdr->dest);
skb_pull(skb, sizeof(struct mctp_i3c_internal_hdr));
xfer.data.in = skb_put(skb, mi->mrl);
/* Make sure netif_rx() is read in the same order as i3c. */
mutex_lock(&mi->lock);
rc = i3c_device_do_priv_xfers(mi->i3c, &xfer, 1); if (rc < 0) goto err;
if (WARN_ON_ONCE(xfer.len > mi->mrl)) { /* Bad i3c bus driver */
rc = -EIO; goto err;
} if (xfer.len < MCTP_I3C_MINLEN) {
stats->rx_length_errors++;
rc = -EIO; goto err;
}
if (mi->have_mdb) { if (payload->len > 0) { if (((u8 *)payload->data)[0] != I3C_MDB_MCTP) { /* Not a mctp-i3c interrupt, ignore it */ return;
}
} else { /* The BCR advertised a Mandatory Data Byte but the *devicedidn'tsendone.
*/
dev_warn_once(i3cdev_to_dev(i3c), "IBI with missing MDB");
}
}
rc = i3c_device_request_ibi(mi->i3c, &ibi); if (rc == -ENOTSUPP) { /* This driver only supports In-Band Interrupt mode. *SupportforPollingModecouldbeaddedifrequired. *(ENOTSUPPisfromthei3clayer,notEOPNOTSUPP).
*/
dev_warn(i3cdev_to_dev(mi->i3c), "Failed, bus driver doesn't support In-Band Interrupts"); goto err;
} elseif (rc < 0) {
dev_err(i3cdev_to_dev(mi->i3c), "Failed requesting IBI (%d)\n", rc); goto err;
}
rc = i3c_device_enable_ibi(mi->i3c); if (rc < 0) { /* Assume a driver supporting request_ibi also *supportsenable_ibi.
*/
dev_err(i3cdev_to_dev(mi->i3c), "Failed enabling IBI (%d)\n", rc); goto err_free_ibi;
}
return0;
err_free_ibi:
i3c_device_free_ibi(mi->i3c);
err: return rc;
}
/* Adds a new MCTP i3c_device to a bus */ staticint mctp_i3c_add_device(struct mctp_i3c_bus *mbus, struct i3c_device *i3c)
__must_hold(&busdevs_lock)
{ struct mctp_i3c_device *mi = NULL; int rc;
/* Look for a known bus */
mutex_lock(&busdevs_lock);
list_for_each_entry(b, &busdevs, list) if (b->bus == i3c->bus) {
mbus = b; break;
}
mutex_unlock(&busdevs_lock);
if (!mbus) { /* probably no "mctp-controller" property on the i3c bus */ return -ENODEV;
}
return mctp_i3c_add_device(mbus, i3c);
}
staticvoid mctp_i3c_remove_device(struct mctp_i3c_device *mi)
__must_hold(&busdevs_lock)
{ /* Ensure the tx thread isn't using the device */
mutex_lock(&mi->lock);
/* Counterpart of mctp_i3c_setup */
i3c_device_disable_ibi(mi->i3c);
i3c_device_free_ibi(mi->i3c);
/* Counterpart of mctp_i3c_add_device */
i3cdev_set_drvdata(mi->i3c, NULL);
list_del(&mi->list);
/* Safe to unlock after removing from the list */
mutex_unlock(&mi->lock);
kfree(mi);
}
pid = get_unaligned_be48(ihdr->dest);
mi = mctp_i3c_lookup(mbus, pid); if (!mi) { /* I3C endpoint went away after the packet was enqueued? */
stats->tx_dropped++; goto out;
}
if (WARN_ON_ONCE(data_len + 1 > MCTP_I3C_MAXBUF)) goto out;
if (data_len + 1 > (unsignedint)mi->mwl) { /* Route MTU was larger than supported by the endpoint */
stats->tx_dropped++; goto out;
}
/* Need a linear buffer with space for the PEC */
xfer.len = data_len + 1; if (skb_tailroom(skb) >= 1) {
skb_put(skb, 1);
data = skb->data;
} else { /* Otherwise need to copy the buffer */
skb_copy_bits(skb, 0, mbus->tx_scratch, skb->len);
data = mbus->tx_scratch;
}
/* Returns the Provisioned Id of a local bus master */ staticint mctp_i3c_bus_local_pid(struct i3c_bus *bus, u64 *ret_pid)
{ struct i3c_dev_desc *master;
master = bus->cur_master; if (WARN_ON_ONCE(!master)) return -ENOENT;
*ret_pid = master->info.pid;
return0;
}
/* Returns an ERR_PTR on failure */ staticstruct mctp_i3c_bus *mctp_i3c_bus_add(struct i3c_bus *bus)
__must_hold(&busdevs_lock)
{ struct mctp_i3c_bus *mbus = NULL; struct net_device *ndev = NULL; char namebuf[IFNAMSIZ];
u8 addr[PID_SIZE]; int rc;
if (!mctp_i3c_is_mctp_controller(bus)) return ERR_PTR(-ENOENT);
err_free_uninit: /* uninit will not get called if a netdev has not been registered, *soweperformthesamembuscleanupmanually.
*/
mctp_i3c_bus_free(mbus);
err_free_netdev:
free_netdev(ndev);
err: return ERR_PTR(rc);
}
staticvoid mctp_i3c_bus_remove(struct mctp_i3c_bus *mbus)
__must_hold(&busdevs_lock)
{ /* Unregister calls through to ndo_uninit -> mctp_i3c_bus_free() */
mctp_unregister_netdev(mbus->ndev);
free_netdev(mbus->ndev); /* mbus is deallocated */
}
/* It is OK for a bus to already exist. That can occur due to *theraceinmod_initbetweennotifierandfor_each_bus
*/ if (!exists)
mctp_i3c_bus_add(bus);
mutex_unlock(&busdevs_lock); return0;
}
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.