drivers/char/ipmi/ipmb_dev_int.c

Source file repositories/reference/linux-study-clean/drivers/char/ipmi/ipmb_dev_int.c

File Facts

System
Linux kernel
Corpus path
drivers/char/ipmi/ipmb_dev_int.c
Extension
.c
Size
9359 bytes
Lines
383
Domain
Driver Families
Bucket
drivers/char
Inferred role
Driver Families: operation-table or driver-model contract
Status
pattern implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

static const struct file_operations ipmb_fops = {
	.owner	= THIS_MODULE,
	.read	= ipmb_read,
	.write	= ipmb_write,
	.poll	= ipmb_poll,
};

/* Called with ipmb_dev->lock held. */
static void ipmb_handle_request(struct ipmb_dev *ipmb_dev)
{
	struct ipmb_request_elem *queue_elem;

	if (atomic_read(&ipmb_dev->request_queue_len) >=
			REQUEST_QUEUE_MAX_LEN)
		return;

	queue_elem = kmalloc_obj(*queue_elem, GFP_ATOMIC);
	if (!queue_elem)
		return;

	memcpy(&queue_elem->request, &ipmb_dev->request,
		sizeof(struct ipmb_msg));
	list_add(&queue_elem->list, &ipmb_dev->request_queue);
	atomic_inc(&ipmb_dev->request_queue_len);
	wake_up_all(&ipmb_dev->wait_queue);
}

static u8 ipmb_verify_checksum1(struct ipmb_dev *ipmb_dev, u8 rs_sa)
{
	/* The 8 lsb of the sum is 0 when the checksum is valid */
	return (rs_sa + ipmb_dev->request.netfn_rs_lun +
		ipmb_dev->request.checksum1);
}

/*
 * Verify if message has proper ipmb header with minimum length
 * and correct checksum byte.
 */
static bool is_ipmb_msg(struct ipmb_dev *ipmb_dev, u8 rs_sa)
{
	if ((ipmb_dev->msg_idx >= IPMB_REQUEST_LEN_MIN) &&
	   (!ipmb_verify_checksum1(ipmb_dev, rs_sa)))
		return true;

	return false;
}

/*
 * The IPMB protocol only supports I2C Writes so there is no need
 * to support I2C_SLAVE_READ* events.
 * This i2c callback function only monitors IPMB request messages
 * and adds them in a queue, so that they can be handled by
 * receive_ipmb_request.
 */
static int ipmb_slave_cb(struct i2c_client *client,
			enum i2c_slave_event event, u8 *val)
{
	struct ipmb_dev *ipmb_dev = i2c_get_clientdata(client);
	u8 *buf = (u8 *)&ipmb_dev->request;
	unsigned long flags;

	spin_lock_irqsave(&ipmb_dev->lock, flags);
	switch (event) {
	case I2C_SLAVE_WRITE_REQUESTED:
		memset(&ipmb_dev->request, 0, sizeof(ipmb_dev->request));
		ipmb_dev->msg_idx = 0;

		/*
		 * At index 0, ipmb_msg stores the length of msg,
		 * skip it for now.
		 * The len will be populated once the whole
		 * buf is populated.
		 *
		 * The I2C bus driver's responsibility is to pass the
		 * data bytes to the backend driver; it does not
		 * forward the i2c slave address.
		 * Since the first byte in the IPMB message is the
		 * address of the responder, it is the responsibility
		 * of the IPMB driver to format the message properly.
		 * So this driver prepends the address of the responder
		 * to the received i2c data before the request message
		 * is handled in userland.
		 */
		buf[++ipmb_dev->msg_idx] = GET_8BIT_ADDR(client->addr);
		break;

	case I2C_SLAVE_WRITE_RECEIVED:
		if (ipmb_dev->msg_idx >= sizeof(struct ipmb_msg) - 1)
			break;

Annotation

Implementation Notes