Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Corey Minyard | 2652 | 99.62% | 6 | 50.00% |
Miaoqian Lin | 5 | 0.19% | 1 | 8.33% |
Stephen Kitt | 1 | 0.04% | 1 | 8.33% |
Linus Torvalds | 1 | 0.04% | 1 | 8.33% |
Colin Ian King | 1 | 0.04% | 1 | 8.33% |
Christophe Jaillet | 1 | 0.04% | 1 | 8.33% |
Uwe Kleine-König | 1 | 0.04% | 1 | 8.33% |
Total | 2662 | 12 |
// SPDX-License-Identifier: GPL-2.0 /* * Driver to talk to a remote management controller on IPMB. */ #include <linux/acpi.h> #include <linux/errno.h> #include <linux/i2c.h> #include <linux/miscdevice.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/poll.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/semaphore.h> #include <linux/kthread.h> #include <linux/wait.h> #include <linux/ipmi_msgdefs.h> #include <linux/ipmi_smi.h> #define DEVICE_NAME "ipmi-ipmb" static int bmcaddr = 0x20; module_param(bmcaddr, int, 0644); MODULE_PARM_DESC(bmcaddr, "Address to use for BMC."); static unsigned int retry_time_ms = 250; module_param(retry_time_ms, uint, 0644); MODULE_PARM_DESC(retry_time_ms, "Timeout time between retries, in milliseconds."); static unsigned int max_retries = 1; module_param(max_retries, uint, 0644); MODULE_PARM_DESC(max_retries, "Max resends of a command before timing out."); /* Add room for the two slave addresses, two checksums, and rqSeq. */ #define IPMB_MAX_MSG_LEN (IPMI_MAX_MSG_LENGTH + 5) struct ipmi_ipmb_dev { struct ipmi_smi *intf; struct i2c_client *client; struct i2c_client *slave; struct ipmi_smi_handlers handlers; bool ready; u8 curr_seq; u8 bmcaddr; u32 retry_time_ms; u32 max_retries; struct ipmi_smi_msg *next_msg; struct ipmi_smi_msg *working_msg; /* Transmit thread. */ struct task_struct *thread; struct semaphore wake_thread; struct semaphore got_rsp; spinlock_t lock; bool stopping; u8 xmitmsg[IPMB_MAX_MSG_LEN]; unsigned int xmitlen; u8 rcvmsg[IPMB_MAX_MSG_LEN]; unsigned int rcvlen; bool overrun; }; static bool valid_ipmb(struct ipmi_ipmb_dev *iidev) { u8 *msg = iidev->rcvmsg; u8 netfn; if (iidev->overrun) return false; /* Minimum message size. */ if (iidev->rcvlen < 7) return false; /* Is it a response? */ netfn = msg[1] >> 2; if (netfn & 1) { /* Response messages have an added completion code. */ if (iidev->rcvlen < 8) return false; } if (ipmb_checksum(msg, 3) != 0) return false; if (ipmb_checksum(msg + 3, iidev->rcvlen - 3) != 0) return false; return true; } static void ipmi_ipmb_check_msg_done(struct ipmi_ipmb_dev *iidev) { struct ipmi_smi_msg *imsg = NULL; u8 *msg = iidev->rcvmsg; bool is_cmd; unsigned long flags; if (iidev->rcvlen == 0) return; if (!valid_ipmb(iidev)) goto done; is_cmd = ((msg[1] >> 2) & 1) == 0; if (is_cmd) { /* Ignore commands until we are up. */ if (!iidev->ready) goto done; /* It's a command, allocate a message for it. */ imsg = ipmi_alloc_smi_msg(); if (!imsg) goto done; imsg->type = IPMI_SMI_MSG_TYPE_IPMB_DIRECT; imsg->data_size = 0; } else { spin_lock_irqsave(&iidev->lock, flags); if (iidev->working_msg) { u8 seq = msg[4] >> 2; bool xmit_rsp = (iidev->working_msg->data[0] >> 2) & 1; /* * Responses should carry the sequence we sent * them with. If it's a transmitted response, * ignore it. And if the message hasn't been * transmitted, ignore it. */ if (!xmit_rsp && seq == iidev->curr_seq) { iidev->curr_seq = (iidev->curr_seq + 1) & 0x3f; imsg = iidev->working_msg; iidev->working_msg = NULL; } } spin_unlock_irqrestore(&iidev->lock, flags); } if (!imsg) goto done; if (imsg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) { imsg->rsp[0] = msg[1]; /* NetFn/LUN */ /* * Keep the source address, rqSeq. Drop the trailing * checksum. */ memcpy(imsg->rsp + 1, msg + 3, iidev->rcvlen - 4); imsg->rsp_size = iidev->rcvlen - 3; } else { imsg->rsp[0] = msg[1]; /* NetFn/LUN */ /* * Skip the source address, rqSeq. Drop the trailing * checksum. */ memcpy(imsg->rsp + 1, msg + 5, iidev->rcvlen - 6); imsg->rsp_size = iidev->rcvlen - 5; } ipmi_smi_msg_received(iidev->intf, imsg); if (!is_cmd) up(&iidev->got_rsp); done: iidev->overrun = false; iidev->rcvlen = 0; } /* * The IPMB protocol only supports i2c writes so there is no need to * support I2C_SLAVE_READ* events, except to know if the other end has * issued a read without going to stop mode. */ static int ipmi_ipmb_slave_cb(struct i2c_client *client, enum i2c_slave_event event, u8 *val) { struct ipmi_ipmb_dev *iidev = i2c_get_clientdata(client); switch (event) { case I2C_SLAVE_WRITE_REQUESTED: ipmi_ipmb_check_msg_done(iidev); /* * First byte is the slave address, to ease the checksum * calculation. */ iidev->rcvmsg[0] = client->addr << 1; iidev->rcvlen = 1; break; case I2C_SLAVE_WRITE_RECEIVED: if (iidev->rcvlen >= sizeof(iidev->rcvmsg)) iidev->overrun = true; else iidev->rcvmsg[iidev->rcvlen++] = *val; break; case I2C_SLAVE_READ_REQUESTED: case I2C_SLAVE_STOP: ipmi_ipmb_check_msg_done(iidev); break; case I2C_SLAVE_READ_PROCESSED: break; } return 0; } static void ipmi_ipmb_send_response(struct ipmi_ipmb_dev *iidev, struct ipmi_smi_msg *msg, u8 cc) { if ((msg->data[0] >> 2) & 1) { /* * It's a response being sent, we need to return a * response to the response. Fake a send msg command * response with channel 0. This will always be ipmb * direct. */ msg->data[0] = (IPMI_NETFN_APP_REQUEST | 1) << 2; msg->data[3] = IPMI_SEND_MSG_CMD; msg->data[4] = cc; msg->data_size = 5; } msg->rsp[0] = msg->data[0] | (1 << 2); if (msg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) { msg->rsp[1] = msg->data[1]; msg->rsp[2] = msg->data[2]; msg->rsp[3] = msg->data[3]; msg->rsp[4] = cc; msg->rsp_size = 5; } else { msg->rsp[1] = msg->data[1]; msg->rsp[2] = cc; msg->rsp_size = 3; } ipmi_smi_msg_received(iidev->intf, msg); } static void ipmi_ipmb_format_for_xmit(struct ipmi_ipmb_dev *iidev, struct ipmi_smi_msg *msg) { if (msg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) { iidev->xmitmsg[0] = msg->data[1]; iidev->xmitmsg[1] = msg->data[0]; memcpy(iidev->xmitmsg + 4, msg->data + 2, msg->data_size - 2); iidev->xmitlen = msg->data_size + 2; } else { iidev->xmitmsg[0] = iidev->bmcaddr; iidev->xmitmsg[1] = msg->data[0]; iidev->xmitmsg[4] = 0; memcpy(iidev->xmitmsg + 5, msg->data + 1, msg->data_size - 1); iidev->xmitlen = msg->data_size + 4; } iidev->xmitmsg[3] = iidev->slave->addr << 1; if (((msg->data[0] >> 2) & 1) == 0) /* If it's a command, put in our own sequence number. */ iidev->xmitmsg[4] = ((iidev->xmitmsg[4] & 0x03) | (iidev->curr_seq << 2)); /* Now add on the final checksums. */ iidev->xmitmsg[2] = ipmb_checksum(iidev->xmitmsg, 2); iidev->xmitmsg[iidev->xmitlen] = ipmb_checksum(iidev->xmitmsg + 3, iidev->xmitlen - 3); iidev->xmitlen++; } static int ipmi_ipmb_thread(void *data) { struct ipmi_ipmb_dev *iidev = data; while (!kthread_should_stop()) { long ret; struct i2c_msg i2c_msg; struct ipmi_smi_msg *msg = NULL; unsigned long flags; unsigned int retries = 0; /* Wait for a message to send */ ret = down_interruptible(&iidev->wake_thread); if (iidev->stopping) break; if (ret) continue; spin_lock_irqsave(&iidev->lock, flags); if (iidev->next_msg) { msg = iidev->next_msg; iidev->next_msg = NULL; } spin_unlock_irqrestore(&iidev->lock, flags); if (!msg) continue; ipmi_ipmb_format_for_xmit(iidev, msg); retry: i2c_msg.len = iidev->xmitlen - 1; if (i2c_msg.len > 32) { ipmi_ipmb_send_response(iidev, msg, IPMI_REQ_LEN_EXCEEDED_ERR); continue; } i2c_msg.addr = iidev->xmitmsg[0] >> 1; i2c_msg.flags = 0; i2c_msg.buf = iidev->xmitmsg + 1; /* Rely on i2c_transfer for a barrier. */ iidev->working_msg = msg; ret = i2c_transfer(iidev->client->adapter, &i2c_msg, 1); if ((msg->data[0] >> 2) & 1) { /* * It's a response, nothing will be returned * by the other end. */ iidev->working_msg = NULL; ipmi_ipmb_send_response(iidev, msg, ret < 0 ? IPMI_BUS_ERR : 0); continue; } if (ret < 0) { iidev->working_msg = NULL; ipmi_ipmb_send_response(iidev, msg, IPMI_BUS_ERR); continue; } /* A command was sent, wait for its response. */ ret = down_timeout(&iidev->got_rsp, msecs_to_jiffies(iidev->retry_time_ms)); /* * Grab the message if we can. If the handler hasn't * already handled it, the message will still be there. */ spin_lock_irqsave(&iidev->lock, flags); msg = iidev->working_msg; iidev->working_msg = NULL; spin_unlock_irqrestore(&iidev->lock, flags); if (!msg && ret) { /* * If working_msg is not set and we timed out, * that means the message grabbed by * check_msg_done before we could grab it * here. Wait again for check_msg_done to up * the semaphore. */ down(&iidev->got_rsp); } else if (msg && ++retries <= iidev->max_retries) { spin_lock_irqsave(&iidev->lock, flags); iidev->working_msg = msg; spin_unlock_irqrestore(&iidev->lock, flags); goto retry; } if (msg) ipmi_ipmb_send_response(iidev, msg, IPMI_TIMEOUT_ERR); } if (iidev->next_msg) /* Return an unspecified error. */ ipmi_ipmb_send_response(iidev, iidev->next_msg, 0xff); return 0; } static int ipmi_ipmb_start_processing(void *send_info, struct ipmi_smi *new_intf) { struct ipmi_ipmb_dev *iidev = send_info; iidev->intf = new_intf; iidev->ready = true; return 0; } static void ipmi_ipmb_stop_thread(struct ipmi_ipmb_dev *iidev) { if (iidev->thread) { struct task_struct *t = iidev->thread; iidev->thread = NULL; iidev->stopping = true; up(&iidev->wake_thread); up(&iidev->got_rsp); kthread_stop(t); } } static void ipmi_ipmb_shutdown(void *send_info) { struct ipmi_ipmb_dev *iidev = send_info; ipmi_ipmb_stop_thread(iidev); } static void ipmi_ipmb_sender(void *send_info, struct ipmi_smi_msg *msg) { struct ipmi_ipmb_dev *iidev = send_info; unsigned long flags; spin_lock_irqsave(&iidev->lock, flags); BUG_ON(iidev->next_msg); iidev->next_msg = msg; spin_unlock_irqrestore(&iidev->lock, flags); up(&iidev->wake_thread); } static void ipmi_ipmb_request_events(void *send_info) { /* We don't fetch events here. */ } static void ipmi_ipmb_cleanup(struct ipmi_ipmb_dev *iidev) { if (iidev->slave) { i2c_slave_unregister(iidev->slave); if (iidev->slave != iidev->client) i2c_unregister_device(iidev->slave); } iidev->slave = NULL; iidev->client = NULL; ipmi_ipmb_stop_thread(iidev); } static void ipmi_ipmb_remove(struct i2c_client *client) { struct ipmi_ipmb_dev *iidev = i2c_get_clientdata(client); ipmi_ipmb_cleanup(iidev); ipmi_unregister_smi(iidev->intf); } static int ipmi_ipmb_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct ipmi_ipmb_dev *iidev; struct device_node *slave_np; struct i2c_adapter *slave_adap = NULL; struct i2c_client *slave = NULL; int rv; iidev = devm_kzalloc(&client->dev, sizeof(*iidev), GFP_KERNEL); if (!iidev) return -ENOMEM; if (of_property_read_u8(dev->of_node, "bmcaddr", &iidev->bmcaddr) != 0) iidev->bmcaddr = bmcaddr; if (iidev->bmcaddr == 0 || iidev->bmcaddr & 1) { /* Can't have the write bit set. */ dev_notice(&client->dev, "Invalid bmc address value %2.2x\n", iidev->bmcaddr); return -EINVAL; } if (of_property_read_u32(dev->of_node, "retry-time", &iidev->retry_time_ms) != 0) iidev->retry_time_ms = retry_time_ms; if (of_property_read_u32(dev->of_node, "max-retries", &iidev->max_retries) != 0) iidev->max_retries = max_retries; slave_np = of_parse_phandle(dev->of_node, "slave-dev", 0); if (slave_np) { slave_adap = of_get_i2c_adapter_by_node(slave_np); of_node_put(slave_np); if (!slave_adap) { dev_notice(&client->dev, "Could not find slave adapter\n"); return -EINVAL; } } iidev->client = client; if (slave_adap) { struct i2c_board_info binfo; memset(&binfo, 0, sizeof(binfo)); strscpy(binfo.type, "ipmb-slave", I2C_NAME_SIZE); binfo.addr = client->addr; binfo.flags = I2C_CLIENT_SLAVE; slave = i2c_new_client_device(slave_adap, &binfo); i2c_put_adapter(slave_adap); if (IS_ERR(slave)) { rv = PTR_ERR(slave); dev_notice(&client->dev, "Could not allocate slave device: %d\n", rv); return rv; } i2c_set_clientdata(slave, iidev); } else { slave = client; } i2c_set_clientdata(client, iidev); slave->flags |= I2C_CLIENT_SLAVE; rv = i2c_slave_register(slave, ipmi_ipmb_slave_cb); if (rv) goto out_err; iidev->slave = slave; slave = NULL; iidev->handlers.flags = IPMI_SMI_CAN_HANDLE_IPMB_DIRECT; iidev->handlers.start_processing = ipmi_ipmb_start_processing; iidev->handlers.shutdown = ipmi_ipmb_shutdown; iidev->handlers.sender = ipmi_ipmb_sender; iidev->handlers.request_events = ipmi_ipmb_request_events; spin_lock_init(&iidev->lock); sema_init(&iidev->wake_thread, 0); sema_init(&iidev->got_rsp, 0); iidev->thread = kthread_run(ipmi_ipmb_thread, iidev, "kipmb%4.4x", client->addr); if (IS_ERR(iidev->thread)) { rv = PTR_ERR(iidev->thread); dev_notice(&client->dev, "Could not start kernel thread: error %d\n", rv); goto out_err; } rv = ipmi_register_smi(&iidev->handlers, iidev, &client->dev, iidev->bmcaddr); if (rv) goto out_err; return 0; out_err: if (slave && slave != client) i2c_unregister_device(slave); ipmi_ipmb_cleanup(iidev); return rv; } #ifdef CONFIG_OF static const struct of_device_id of_ipmi_ipmb_match[] = { { .type = "ipmi", .compatible = DEVICE_NAME }, {}, }; MODULE_DEVICE_TABLE(of, of_ipmi_ipmb_match); #else #define of_ipmi_ipmb_match NULL #endif static const struct i2c_device_id ipmi_ipmb_id[] = { { DEVICE_NAME, 0 }, {}, }; MODULE_DEVICE_TABLE(i2c, ipmi_ipmb_id); static struct i2c_driver ipmi_ipmb_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = DEVICE_NAME, .of_match_table = of_ipmi_ipmb_match, }, .probe_new = ipmi_ipmb_probe, .remove = ipmi_ipmb_remove, .id_table = ipmi_ipmb_id, }; module_i2c_driver(ipmi_ipmb_driver); MODULE_AUTHOR("Corey Minyard"); MODULE_DESCRIPTION("IPMI IPMB driver"); MODULE_LICENSE("GPL v2");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1