<div dir="ltr">> <span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif">There is talk of putting the bt-host driver into drivers/char/ipmi. I</span><br class="gmail_msg" style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif"><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif">> imagine that will affect your work.</span><div><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif"><br></span></div><div><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif">Yes it would; this was talked about more on the cover letter, so I will continue this discussion there.</span></div><div><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif"><br></span></div><div><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif">> </span><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif;line-height:1.5">I didn't pick up on this when reading your cover letter. The host side</span></div><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif">> will read the messages straight into the kernel and userspace, not</span><br class="gmail_msg" style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif"><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif">> pass them through firmware?</span><div><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif;line-height:1.5"><br></span></div><div><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif;line-height:1.5">That is the plan at this time. If the BIOS were to want to communicate with the BMC they would obviously have to implement the master side of this driver. I think it is useful to</span></div><div><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif;line-height:1.5">  a) provide a reference implementation in the kernel</span></div><div><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif;line-height:1.5">  b) not make any assumptions about something like BIOS runtime services</span></div><div><font color="#212121" face="helvetica neue, helvetica, arial, sans-serif">In any case, the slave side does not care how the master organizes its communication with the BMC.</font></div><div><font color="#212121" face="helvetica neue, helvetica, arial, sans-serif"><br></font></div><div><font color="#212121" face="helvetica neue, helvetica, arial, sans-serif">> </font><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif">It feels like there should be a neater way of doing this read/write</span><br class="gmail_msg" style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif"><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif">> and wait operation that the driver does. I don't have a specific</span><br class="gmail_msg" style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif"><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif">> recommendation though.</span></div><div><font color="#212121" face="helvetica neue, helvetica, arial, sans-serif"><br></font></div><div><font color="#212121" face="helvetica neue, helvetica, arial, sans-serif">I do not think the write is particularly messy, maybe I am being presumptuous about the amount of errors that an I2C bus may experience. I can appreciate your concern about the read though; I was not thrilled about having to poll for a non-zero length, but without SMBus Alert we do not have a way of knowing whether a read is ready without polling (I fully intend on adding support for that later on, but that requires a change to I2C core and I would really like to keep that separate :-) ).</font><br></div><div><font color="#212121" face="helvetica neue, helvetica, arial, sans-serif"><br></font></div><div><font color="#212121" face="helvetica neue, helvetica, arial, sans-serif">> </font><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif;line-height:1.5">The goto doesn't do much :)</span></div><div><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif;line-height:1.5"><br></span></div><div><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif;line-height:1.5">No it does not, but I thought it made it clear that it was intentionally failing out rather than proceeding normally. I guess I thought it better fit past criticism about explicit fail out (I am thinking of "goto out" over "cleanup return" in the I2C driver). In any case, I do not feel strongly about it, but I guess I would like an explanation.</span></div><div><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif;line-height:1.5"><br></span></div><div><span style="color:rgb(33,33,33);font-family:"helvetica neue",helvetica,arial,sans-serif;line-height:1.5">Thanks!</span></div></div><br><div class="gmail_quote"><div dir="ltr">On Thu, Sep 8, 2016 at 10:51 PM Joel Stanley <<a href="mailto:joel@jms.id.au">joel@jms.id.au</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">On Fri, Sep 9, 2016 at 6:58 AM, Brendan Higgins<br class="gmail_msg">
<<a href="mailto:brendanhiggins@google.com" class="gmail_msg" target="_blank">brendanhiggins@google.com</a>> wrote:<br class="gmail_msg">
> The IPMI definition of the Block Transfer protocol defines the hardware<br class="gmail_msg">
> registers and behavior in addition to the message format and messaging<br class="gmail_msg">
> semantics. This implements a new protocol that uses IPMI Block Transfer<br class="gmail_msg">
> messages and semantics on top of a standard I2C interface. This protocol<br class="gmail_msg">
> has the same BMC side file system interface as "bt-host".<br class="gmail_msg">
><br class="gmail_msg">
> Signed-off-by: Brendan Higgins <<a href="mailto:brendanhiggins@google.com" class="gmail_msg" target="_blank">brendanhiggins@google.com</a>><br class="gmail_msg">
> ---<br class="gmail_msg">
>  drivers/misc/Kconfig         |  23 ++++<br class="gmail_msg">
>  drivers/misc/Makefile        |   2 +<br class="gmail_msg">
>  drivers/misc/bt-i2c-master.c | 199 +++++++++++++++++++++++++++<br class="gmail_msg">
>  drivers/misc/bt-i2c-slave.c  | 317 +++++++++++++++++++++++++++++++++++++++++++<br class="gmail_msg">
>  drivers/misc/bt.h            |  19 +++<br class="gmail_msg">
>  5 files changed, 560 insertions(+)<br class="gmail_msg">
>  create mode 100644 drivers/misc/bt-i2c-master.c<br class="gmail_msg">
>  create mode 100644 drivers/misc/bt-i2c-slave.c<br class="gmail_msg">
>  create mode 100644 drivers/misc/bt.h<br class="gmail_msg">
><br class="gmail_msg">
<br class="gmail_msg">
There is talk of putting the bt-host driver into drivers/char/ipmi. I<br class="gmail_msg">
imagine that will affect your work.<br class="gmail_msg">
<br class="gmail_msg">
> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig<br class="gmail_msg">
> index 4617ddc..1c7e3a9 100644<br class="gmail_msg">
> --- a/drivers/misc/Kconfig<br class="gmail_msg">
> +++ b/drivers/misc/Kconfig<br class="gmail_msg">
> @@ -809,6 +809,29 @@ config ASPEED_BT_IPMI_HOST<br class="gmail_msg">
>         help<br class="gmail_msg">
>           Support for the Aspeed BT ipmi host.<br class="gmail_msg">
><br class="gmail_msg">
> +config BT_I2C_SLAVE<br class="gmail_msg">
> +       depends on !ASPEED_BT_IPMI_HOST && I2C_SLAVE<br class="gmail_msg">
> +       tristate "BT I2C IPMI slave driver"<br class="gmail_msg">
> +       default "n"<br class="gmail_msg">
> +       ---help---<br class="gmail_msg">
> +         Block Transfer over I2C defines a new IPMI compatible interface that<br class="gmail_msg">
> +         uses Block Transfer messages and semantics on top of plain old I2C.<br class="gmail_msg">
> +<br class="gmail_msg">
> +         This adds support for the slave side of the protocol (only really<br class="gmail_msg">
> +         useful for BMCs (Baseboard Management Controllers)).<br class="gmail_msg">
> +<br class="gmail_msg">
> +config BT_I2C_MASTER<br class="gmail_msg">
> +       depends on I2C<br class="gmail_msg">
> +       tristate "BT I2C IPMI master driver"<br class="gmail_msg">
> +       default "n"<br class="gmail_msg">
> +       ---help---<br class="gmail_msg">
> +         Block Transfer over I2C defines a new IPMI compatible interface that<br class="gmail_msg">
> +         uses Block Transfer messages and semantics on top of plain old I2C.<br class="gmail_msg">
> +<br class="gmail_msg">
> +         This adds support for master side of the protocol (this is most likely<br class="gmail_msg">
> +         what you want if you are compiling for a regular CPU, only useful if<br class="gmail_msg">
> +         your computer has a BMC that speaks the slave side of this protocol).<br class="gmail_msg">
<br class="gmail_msg">
I didn't pick up on this when reading your cover letter. The host side<br class="gmail_msg">
will read the messages straight into the kernel and userspace, not<br class="gmail_msg">
pass them through firmware?<br class="gmail_msg">
<br class="gmail_msg">
> +<br class="gmail_msg">
>  source "drivers/misc/c2port/Kconfig"<br class="gmail_msg">
>  source "drivers/misc/eeprom/Kconfig"<br class="gmail_msg">
>  source "drivers/misc/cb710/Kconfig"<br class="gmail_msg">
> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile<br class="gmail_msg">
> index 724861b..b642ed8 100644<br class="gmail_msg">
> --- a/drivers/misc/Makefile<br class="gmail_msg">
> +++ b/drivers/misc/Makefile<br class="gmail_msg">
> @@ -58,3 +58,5 @@ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o<br class="gmail_msg">
>  obj-$(CONFIG_CXL_BASE)         += cxl/<br class="gmail_msg">
>  obj-$(CONFIG_PANEL)             += panel.o<br class="gmail_msg">
>  obj-$(CONFIG_ASPEED_BT_IPMI_HOST)      += bt-host.o<br class="gmail_msg">
> +obj-$(CONFIG_BT_I2C_SLAVE)     += bt-i2c-slave.o<br class="gmail_msg">
> +obj-$(CONFIG_BT_I2C_MASTER)    += bt-i2c-master.o<br class="gmail_msg">
> diff --git a/drivers/misc/bt-i2c-master.c b/drivers/misc/bt-i2c-master.c<br class="gmail_msg">
> new file mode 100644<br class="gmail_msg">
> index 0000000..22142d6<br class="gmail_msg">
> --- /dev/null<br class="gmail_msg">
> +++ b/drivers/misc/bt-i2c-master.c<br class="gmail_msg">
> @@ -0,0 +1,199 @@<br class="gmail_msg">
> +#include <linux/module.h><br class="gmail_msg">
> +#include <linux/moduleparam.h><br class="gmail_msg">
> +#include <linux/errno.h><br class="gmail_msg">
> +#include <linux/poll.h><br class="gmail_msg">
> +#include <linux/mutex.h><br class="gmail_msg">
> +#include <linux/slab.h><br class="gmail_msg">
> +#include <linux/i2c.h><br class="gmail_msg">
> +#include <linux/init.h><br class="gmail_msg">
> +#include <linux/device.h><br class="gmail_msg">
> +#include <linux/of.h><br class="gmail_msg">
> +#include <linux/platform_device.h><br class="gmail_msg">
> +#include <linux/io.h><br class="gmail_msg">
> +#include <linux/delay.h><br class="gmail_msg">
> +#include <linux/miscdevice.h><br class="gmail_msg">
> +#include <linux/timer.h><br class="gmail_msg">
> +#include <linux/jiffies.h><br class="gmail_msg">
> +<br class="gmail_msg">
> +#include "bt.h"<br class="gmail_msg">
> +<br class="gmail_msg">
> +#define DEVICE_NAME    "bt-master"<br class="gmail_msg">
> +<br class="gmail_msg">
> +struct bt_i2c_master {<br class="gmail_msg">
> +       struct i2c_client       *client;<br class="gmail_msg">
> +       struct miscdevice       miscdev;<br class="gmail_msg">
> +       struct mutex            file_mutex;<br class="gmail_msg">
> +};<br class="gmail_msg">
> +<br class="gmail_msg">
> +static const unsigned long write_timeout = 25;<br class="gmail_msg">
> +<br class="gmail_msg">
> +static int send_bt_request(struct bt_i2c_master *bt_master,<br class="gmail_msg">
> +                          struct bt_msg *bt_request)<br class="gmail_msg">
> +{<br class="gmail_msg">
> +       struct i2c_client *client = bt_master->client;<br class="gmail_msg">
> +       u8 *buf = (u8 *) bt_request;<br class="gmail_msg">
> +       unsigned long timeout, read_time;<br class="gmail_msg">
> +       int ret;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       timeout = jiffies + msecs_to_jiffies(write_timeout);<br class="gmail_msg">
> +       do {<br class="gmail_msg">
> +               read_time = jiffies;<br class="gmail_msg">
> +               ret = i2c_master_send(client, buf, bt_msg_len(bt_request));<br class="gmail_msg">
> +               if (ret >= 0)<br class="gmail_msg">
> +                       return 0;<br class="gmail_msg">
> +               usleep_range(1000, 1500);<br class="gmail_msg">
> +       } while (time_before(read_time, timeout));<br class="gmail_msg">
<br class="gmail_msg">
It feels like there should be a neater way of doing this read/write<br class="gmail_msg">
and wait operation that the driver does. I don't have a specific<br class="gmail_msg">
recommendation though.<br class="gmail_msg">
<br class="gmail_msg">
> +       return ret;<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +static int receive_bt_response(struct bt_i2c_master *bt_master,<br class="gmail_msg">
> +                              struct bt_msg *bt_response)<br class="gmail_msg">
> +{<br class="gmail_msg">
> +       struct i2c_client *client = bt_master->client;<br class="gmail_msg">
> +       u8 *buf = (u8 *) bt_response;<br class="gmail_msg">
> +       u8 len = 0;<br class="gmail_msg">
> +       unsigned long timeout, read_time;<br class="gmail_msg">
> +       int ret;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       /*<br class="gmail_msg">
> +        * Slave may not NACK when not ready, so we peek at the first byte to<br class="gmail_msg">
> +        * see if it is a valid length.<br class="gmail_msg">
> +        */<br class="gmail_msg">
> +       while (len == 0) {<br class="gmail_msg">
> +               i2c_master_recv(client, &len, 1);<br class="gmail_msg">
> +               if (len != 0)<br class="gmail_msg">
> +                       break;<br class="gmail_msg">
> +               usleep_range(1000, 1500);<br class="gmail_msg">
> +<br class="gmail_msg">
> +               /* Signal received: quit syscall. */<br class="gmail_msg">
> +               if (signal_pending(current))<br class="gmail_msg">
> +                       return -ERESTARTSYS;<br class="gmail_msg">
> +       }<br class="gmail_msg">
> +<br class="gmail_msg">
> +       timeout = jiffies + msecs_to_jiffies(write_timeout);<br class="gmail_msg">
> +       do {<br class="gmail_msg">
> +               read_time = jiffies;<br class="gmail_msg">
> +               ret = i2c_master_recv(client, buf, len + 1);<br class="gmail_msg">
> +               if (ret >= 0)<br class="gmail_msg">
> +                       return 0;<br class="gmail_msg">
> +               usleep_range(1000, 1500);<br class="gmail_msg">
> +       } while (time_before(read_time, timeout));<br class="gmail_msg">
> +       return ret;<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +static inline struct bt_i2c_master *to_bt_i2c_master(struct file *file)<br class="gmail_msg">
> +{<br class="gmail_msg">
> +       return container_of(file->private_data, struct bt_i2c_master, miscdev);<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +static ssize_t bt_read(struct file *file, char __user *buf, size_t count,<br class="gmail_msg">
> +                      loff_t *ppos)<br class="gmail_msg">
> +{<br class="gmail_msg">
> +       struct bt_i2c_master *bt_master = to_bt_i2c_master(file);<br class="gmail_msg">
> +       struct bt_msg msg;<br class="gmail_msg">
> +       int ret;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       mutex_lock(&bt_master->file_mutex);<br class="gmail_msg">
> +       ret = receive_bt_response(bt_master, &msg);<br class="gmail_msg">
> +       if (ret < 0)<br class="gmail_msg">
> +               goto out;<br class="gmail_msg">
> +       count = min(count, bt_msg_len(&msg));<br class="gmail_msg">
> +       if (copy_to_user(buf, &msg, count)) {<br class="gmail_msg">
> +               ret = -EFAULT;<br class="gmail_msg">
> +               goto out;<br class="gmail_msg">
<br class="gmail_msg">
The goto doesn't do much :)<br class="gmail_msg">
<br class="gmail_msg">
> +       }<br class="gmail_msg">
> +<br class="gmail_msg">
> +out:<br class="gmail_msg">
> +       mutex_unlock(&bt_master->file_mutex);<br class="gmail_msg">
> +       if (ret < 0)<br class="gmail_msg">
> +               return ret;<br class="gmail_msg">
> +       else<br class="gmail_msg">
> +               return count;<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +static ssize_t bt_write(struct file *file, const char __user *buf, size_t count,<br class="gmail_msg">
> +                       loff_t *ppos)<br class="gmail_msg">
> +{<br class="gmail_msg">
> +       struct bt_i2c_master *bt_master = to_bt_i2c_master(file);<br class="gmail_msg">
> +       struct bt_msg msg;<br class="gmail_msg">
> +       int ret;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       mutex_lock(&bt_master->file_mutex);<br class="gmail_msg">
> +       if (count > sizeof(struct bt_msg)) {<br class="gmail_msg">
> +               ret = -EFAULT;<br class="gmail_msg">
> +               goto out;<br class="gmail_msg">
> +       }<br class="gmail_msg">
> +       if (copy_from_user(&msg, buf, count) || count < bt_msg_len(&msg)) {<br class="gmail_msg">
> +               ret = -EFAULT;<br class="gmail_msg">
> +               goto out;<br class="gmail_msg">
> +       }<br class="gmail_msg">
> +       ret = send_bt_request(bt_master, &msg);<br class="gmail_msg">
> +       if (ret < 0)<br class="gmail_msg">
> +               goto out;<br class="gmail_msg">
> +<br class="gmail_msg">
> +out:<br class="gmail_msg">
> +       mutex_unlock(&bt_master->file_mutex);<br class="gmail_msg">
> +       if (ret < 0)<br class="gmail_msg">
> +               return ret;<br class="gmail_msg">
> +       else<br class="gmail_msg">
> +               return count;<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +static const struct file_operations bt_fops = {<br class="gmail_msg">
> +       .owner          = THIS_MODULE,<br class="gmail_msg">
> +       .read           = bt_read,<br class="gmail_msg">
> +       .write          = bt_write,<br class="gmail_msg">
> +};<br class="gmail_msg">
> +<br class="gmail_msg">
> +static int bt_i2c_probe(struct i2c_client *client,<br class="gmail_msg">
> +                       const struct i2c_device_id *id)<br class="gmail_msg">
> +{<br class="gmail_msg">
> +       struct bt_i2c_master *bt_master;<br class="gmail_msg">
> +       int ret;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       bt_master = devm_kzalloc(&client->dev, sizeof(struct bt_i2c_master),<br class="gmail_msg">
> +                                GFP_KERNEL);<br class="gmail_msg">
> +       if (!bt_master)<br class="gmail_msg">
> +               return -ENOMEM;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       mutex_init(&bt_master->file_mutex);<br class="gmail_msg">
> +       bt_master->client = client;<br class="gmail_msg">
> +       i2c_set_clientdata(client, bt_master);<br class="gmail_msg">
> +<br class="gmail_msg">
> +       bt_master->miscdev.minor = MISC_DYNAMIC_MINOR;<br class="gmail_msg">
> +       bt_master-><a href="http://miscdev.name" rel="noreferrer" class="gmail_msg" target="_blank">miscdev.name</a> = DEVICE_NAME;<br class="gmail_msg">
> +       bt_master->miscdev.fops = &bt_fops;<br class="gmail_msg">
> +       bt_master->miscdev.parent = &client->dev;<br class="gmail_msg">
> +       ret = misc_register(&bt_master->miscdev);<br class="gmail_msg">
> +       if (ret < 0)<br class="gmail_msg">
> +               return ret;<br class="gmail_msg">
> +       return 0;<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +static int bt_i2c_remove(struct i2c_client *client)<br class="gmail_msg">
> +{<br class="gmail_msg">
> +       struct bt_i2c_master *bt_master = i2c_get_clientdata(client);<br class="gmail_msg">
> +<br class="gmail_msg">
> +       misc_deregister(&bt_master->miscdev);<br class="gmail_msg">
> +       return 0;<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +static const struct i2c_device_id bt_i2c_id[] = {<br class="gmail_msg">
> +       {"bt-i2c-master", 0},<br class="gmail_msg">
> +       {},<br class="gmail_msg">
> +};<br class="gmail_msg">
> +MODULE_DEVICE_TABLE(i2c, bt_i2c_id);<br class="gmail_msg">
> +<br class="gmail_msg">
> +static struct i2c_driver bt_i2c_driver = {<br class="gmail_msg">
> +       .driver = {<br class="gmail_msg">
> +               .name           = "bt-i2c-master",<br class="gmail_msg">
> +       },<br class="gmail_msg">
> +       .probe          = bt_i2c_probe,<br class="gmail_msg">
> +       .remove         = bt_i2c_remove,<br class="gmail_msg">
> +       .id_table       = bt_i2c_id,<br class="gmail_msg">
> +};<br class="gmail_msg">
> +module_i2c_driver(bt_i2c_driver);<br class="gmail_msg">
> +<br class="gmail_msg">
> +MODULE_AUTHOR("Brendan Higgins <<a href="mailto:brendanhiggins@google.com" class="gmail_msg" target="_blank">brendanhiggins@google.com</a>>");<br class="gmail_msg">
> +MODULE_DESCRIPTION("IPMI Block Transfer over I2C master.");<br class="gmail_msg">
> +MODULE_LICENSE("GPL v2");<br class="gmail_msg">
> diff --git a/drivers/misc/bt-i2c-slave.c b/drivers/misc/bt-i2c-slave.c<br class="gmail_msg">
> new file mode 100644<br class="gmail_msg">
> index 0000000..1a90e05<br class="gmail_msg">
> --- /dev/null<br class="gmail_msg">
> +++ b/drivers/misc/bt-i2c-slave.c<br class="gmail_msg">
> @@ -0,0 +1,317 @@<br class="gmail_msg">
> +#include <linux/module.h><br class="gmail_msg">
> +#include <linux/moduleparam.h><br class="gmail_msg">
> +#include <linux/errno.h><br class="gmail_msg">
> +#include <linux/poll.h><br class="gmail_msg">
> +#include <linux/spinlock.h><br class="gmail_msg">
> +#include <linux/mutex.h><br class="gmail_msg">
> +#include <linux/slab.h><br class="gmail_msg">
> +#include <linux/i2c.h><br class="gmail_msg">
> +#include <linux/init.h><br class="gmail_msg">
> +#include <linux/device.h><br class="gmail_msg">
> +#include <linux/of.h><br class="gmail_msg">
> +#include <linux/platform_device.h><br class="gmail_msg">
> +#include <linux/io.h><br class="gmail_msg">
> +#include <linux/delay.h><br class="gmail_msg">
> +#include <linux/miscdevice.h><br class="gmail_msg">
> +#include <linux/timer.h><br class="gmail_msg">
> +#include <linux/jiffies.h><br class="gmail_msg">
> +<br class="gmail_msg">
> +#include "bt.h"<br class="gmail_msg">
> +<br class="gmail_msg">
> +/*<br class="gmail_msg">
> + * TODO: This is "bt-host" to match the bt-host driver; however, I think this is<br class="gmail_msg">
> + * unclear in the context of a CPU side driver. Should probably name this<br class="gmail_msg">
> + * and the DEVICE_NAME in bt-host to something like "bt-bmc" or "bt-slave".<br class="gmail_msg">
> + */<br class="gmail_msg">
> +#define DEVICE_NAME    "bt-host"<br class="gmail_msg">
> +<br class="gmail_msg">
> +static const unsigned long request_queue_max_len = 256;<br class="gmail_msg">
> +<br class="gmail_msg">
> +struct bt_request_elem {<br class="gmail_msg">
> +       struct list_head        list;<br class="gmail_msg">
> +       struct bt_msg           request;<br class="gmail_msg">
> +};<br class="gmail_msg">
> +<br class="gmail_msg">
> +struct bt_i2c_slave {<br class="gmail_msg">
> +       struct i2c_client       *client;<br class="gmail_msg">
> +       struct miscdevice       miscdev;<br class="gmail_msg">
> +       struct bt_msg           request;<br class="gmail_msg">
> +       struct list_head        request_queue;<br class="gmail_msg">
> +       atomic_t                request_queue_len;<br class="gmail_msg">
> +       struct bt_msg           response;<br class="gmail_msg">
> +       atomic_t                response_in_progress;<br class="gmail_msg">
> +       size_t                  msg_idx;<br class="gmail_msg">
> +       spinlock_t              lock;<br class="gmail_msg">
> +       wait_queue_head_t       wait_queue;<br class="gmail_msg">
> +       struct mutex            file_mutex;<br class="gmail_msg">
> +};<br class="gmail_msg">
> +<br class="gmail_msg">
> +static int receive_bt_request(struct bt_i2c_slave *bt_slave, bool non_blocking,<br class="gmail_msg">
> +                             struct bt_msg *bt_request) {<br class="gmail_msg">
> +       unsigned long flags;<br class="gmail_msg">
> +       struct bt_request_elem *queue_elem;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       if (!atomic_read(&bt_slave->request_queue_len) && non_blocking)<br class="gmail_msg">
> +               return -EAGAIN;<br class="gmail_msg">
> +       else if (!non_blocking)<br class="gmail_msg">
> +               wait_event_interruptible(<br class="gmail_msg">
> +                               bt_slave->wait_queue,<br class="gmail_msg">
> +                               atomic_read(&bt_slave->request_queue_len));<br class="gmail_msg">
> +<br class="gmail_msg">
> +       spin_lock_irqsave(&bt_slave->lock, flags);<br class="gmail_msg">
> +       queue_elem = list_first_entry(&bt_slave->request_queue,<br class="gmail_msg">
> +                                     struct bt_request_elem, list);<br class="gmail_msg">
> +       memcpy(bt_request, &queue_elem->request, sizeof(struct bt_msg));<br class="gmail_msg">
> +       list_del(&queue_elem->list);<br class="gmail_msg">
> +       kfree(queue_elem);<br class="gmail_msg">
> +       atomic_dec(&bt_slave->request_queue_len);<br class="gmail_msg">
> +       spin_unlock_irqrestore(&bt_slave->lock, flags);<br class="gmail_msg">
> +       return 0;<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +static int send_bt_response(struct bt_i2c_slave *bt_slave, bool non_blocking,<br class="gmail_msg">
> +                           struct bt_msg *bt_response) {<br class="gmail_msg">
> +       unsigned long flags;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       if (non_blocking && atomic_read(&bt_slave->response_in_progress))<br class="gmail_msg">
> +               return -EAGAIN;<br class="gmail_msg">
> +       else if (!non_blocking)<br class="gmail_msg">
> +               wait_event_interruptible(<br class="gmail_msg">
> +                               bt_slave->wait_queue,<br class="gmail_msg">
> +                               !atomic_read(&bt_slave->response_in_progress));<br class="gmail_msg">
> +<br class="gmail_msg">
> +       spin_lock_irqsave(&bt_slave->lock, flags);<br class="gmail_msg">
> +       memcpy(&bt_slave->response, bt_response, sizeof(struct bt_msg));<br class="gmail_msg">
> +       atomic_set(&bt_slave->response_in_progress, 0);<br class="gmail_msg">
> +       spin_unlock_irqrestore(&bt_slave->lock, flags);<br class="gmail_msg">
> +       return 0;<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +static inline struct bt_i2c_slave *to_bt_i2c_slave(struct file *file)<br class="gmail_msg">
> +{<br class="gmail_msg">
> +       return container_of(file->private_data, struct bt_i2c_slave, miscdev);<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +static ssize_t bt_read(struct file *file, char __user *buf, size_t count,<br class="gmail_msg">
> +                      loff_t *ppos)<br class="gmail_msg">
> +{<br class="gmail_msg">
> +       struct bt_i2c_slave *bt_slave = to_bt_i2c_slave(file);<br class="gmail_msg">
> +       struct bt_msg msg;<br class="gmail_msg">
> +       ssize_t ret;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       mutex_lock(&bt_slave->file_mutex);<br class="gmail_msg">
> +       ret = receive_bt_request(bt_slave, file->f_flags & O_NONBLOCK, &msg);<br class="gmail_msg">
> +       if (ret < 0)<br class="gmail_msg">
> +               goto out;<br class="gmail_msg">
> +       count = min(count, bt_msg_len(&msg));<br class="gmail_msg">
> +       if (copy_to_user(buf, &msg, count)) {<br class="gmail_msg">
> +               ret = -EFAULT;<br class="gmail_msg">
> +               goto out;<br class="gmail_msg">
> +       }<br class="gmail_msg">
> +<br class="gmail_msg">
> +out:<br class="gmail_msg">
> +       mutex_unlock(&bt_slave->file_mutex);<br class="gmail_msg">
> +       if (ret < 0)<br class="gmail_msg">
> +               return ret;<br class="gmail_msg">
> +       else<br class="gmail_msg">
> +               return count;<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +static ssize_t bt_write(struct file *file, const char __user *buf, size_t count,<br class="gmail_msg">
> +                       loff_t *ppos)<br class="gmail_msg">
> +{<br class="gmail_msg">
> +       struct bt_i2c_slave *bt_slave = to_bt_i2c_slave(file);<br class="gmail_msg">
> +       struct bt_msg msg;<br class="gmail_msg">
> +       ssize_t ret;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       if (count > sizeof(struct bt_msg))<br class="gmail_msg">
> +               return -EFAULT;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       if (copy_from_user(&msg, buf, count) || count < bt_msg_len(&msg))<br class="gmail_msg">
> +               return -EFAULT;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       mutex_lock(&bt_slave->file_mutex);<br class="gmail_msg">
> +       ret = send_bt_response(bt_slave, file->f_flags & O_NONBLOCK, &msg);<br class="gmail_msg">
> +       mutex_unlock(&bt_slave->file_mutex);<br class="gmail_msg">
> +<br class="gmail_msg">
> +       if (ret < 0)<br class="gmail_msg">
> +               return ret;<br class="gmail_msg">
> +       else<br class="gmail_msg">
> +               return count;<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +static long bt_ioctl(struct file *file, unsigned int cmd, unsigned long param)<br class="gmail_msg">
> +{<br class="gmail_msg">
> +       return 0;<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +static unsigned int bt_poll(struct file *file, poll_table *wait)<br class="gmail_msg">
> +{<br class="gmail_msg">
> +       struct bt_i2c_slave *bt_slave = to_bt_i2c_slave(file);<br class="gmail_msg">
> +       unsigned int mask = 0;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       mutex_lock(&bt_slave->file_mutex);<br class="gmail_msg">
> +       poll_wait(file, &bt_slave->wait_queue, wait);<br class="gmail_msg">
> +<br class="gmail_msg">
> +       if (atomic_read(&bt_slave->request_queue_len))<br class="gmail_msg">
> +               mask |= POLLIN;<br class="gmail_msg">
> +       if (!atomic_read(&bt_slave->response_in_progress))<br class="gmail_msg">
> +               mask |= POLLOUT;<br class="gmail_msg">
> +       mutex_unlock(&bt_slave->file_mutex);<br class="gmail_msg">
> +       return mask;<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +static const struct file_operations bt_fops = {<br class="gmail_msg">
> +       .owner          = THIS_MODULE,<br class="gmail_msg">
> +       .read           = bt_read,<br class="gmail_msg">
> +       .write          = bt_write,<br class="gmail_msg">
> +       .poll           = bt_poll,<br class="gmail_msg">
> +       .unlocked_ioctl = bt_ioctl,<br class="gmail_msg">
> +};<br class="gmail_msg">
> +<br class="gmail_msg">
> +static int handle_request(struct bt_i2c_slave *bt_slave)<br class="gmail_msg">
> +{<br class="gmail_msg">
> +       struct bt_request_elem *queue_elem;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       if (atomic_read(&bt_slave->request_queue_len) >= request_queue_max_len)<br class="gmail_msg">
> +               return -EFAULT;<br class="gmail_msg">
> +       queue_elem = kmalloc(sizeof(struct bt_request_elem), GFP_KERNEL);<br class="gmail_msg">
> +       memcpy(&queue_elem->request, &bt_slave->request, sizeof(struct bt_msg));<br class="gmail_msg">
> +       list_add(&queue_elem->list, &bt_slave->request_queue);<br class="gmail_msg">
> +       atomic_inc(&bt_slave->request_queue_len);<br class="gmail_msg">
> +       wake_up_all(&bt_slave->wait_queue);<br class="gmail_msg">
> +       return 0;<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +static int complete_response(struct bt_i2c_slave *bt_slave)<br class="gmail_msg">
> +{<br class="gmail_msg">
> +       /* Invalidate response in buffer to denote it having been sent. */<br class="gmail_msg">
> +       bt_slave->response.len = 0;<br class="gmail_msg">
> +       atomic_set(&bt_slave->response_in_progress, 1);<br class="gmail_msg">
> +       wake_up_all(&bt_slave->wait_queue);<br class="gmail_msg">
> +       return 0;<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +static int bt_i2c_slave_cb(struct i2c_client *client,<br class="gmail_msg">
> +                          enum i2c_slave_event event, u8 *val)<br class="gmail_msg">
> +{<br class="gmail_msg">
> +       struct bt_i2c_slave *bt_slave = i2c_get_clientdata(client);<br class="gmail_msg">
> +       u8 *buf;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       spin_lock(&bt_slave->lock);<br class="gmail_msg">
> +       switch (event) {<br class="gmail_msg">
> +       case I2C_SLAVE_WRITE_REQUESTED:<br class="gmail_msg">
> +               bt_slave->msg_idx = 0;<br class="gmail_msg">
> +               break;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       case I2C_SLAVE_WRITE_RECEIVED:<br class="gmail_msg">
> +               buf = (u8 *) &bt_slave->request;<br class="gmail_msg">
> +               if (bt_slave->msg_idx >= sizeof(struct bt_i2c_slave))<br class="gmail_msg">
> +                       break;<br class="gmail_msg">
> +<br class="gmail_msg">
> +               buf[bt_slave->msg_idx++] = *val;<br class="gmail_msg">
> +               if (bt_slave->msg_idx >= bt_msg_len(&bt_slave->request))<br class="gmail_msg">
> +                       handle_request(bt_slave);<br class="gmail_msg">
> +               break;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       case I2C_SLAVE_READ_REQUESTED:<br class="gmail_msg">
> +               buf = (u8 *) &bt_slave->response;<br class="gmail_msg">
> +               bt_slave->msg_idx = 0;<br class="gmail_msg">
> +               *val = buf[bt_slave->msg_idx];<br class="gmail_msg">
> +               /*<br class="gmail_msg">
> +                * Do not increment buffer_idx here, because we don't know if<br class="gmail_msg">
> +                * this byte will be actually used. Read Linux I2C slave docs<br class="gmail_msg">
> +                * for details.<br class="gmail_msg">
> +                */<br class="gmail_msg">
> +               break;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       case I2C_SLAVE_READ_PROCESSED:<br class="gmail_msg">
> +               buf = (u8 *) &bt_slave->response;<br class="gmail_msg">
> +               if (bt_slave->response.len &&<br class="gmail_msg">
> +                   bt_slave->msg_idx < bt_msg_len(&bt_slave->response)) {<br class="gmail_msg">
> +                       *val = buf[++bt_slave->msg_idx];<br class="gmail_msg">
> +               } else {<br class="gmail_msg">
> +                       *val = 0;<br class="gmail_msg">
> +               }<br class="gmail_msg">
> +               if (bt_slave->msg_idx + 1 >= bt_msg_len(&bt_slave->response))<br class="gmail_msg">
> +                       complete_response(bt_slave);<br class="gmail_msg">
> +               break;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       case I2C_SLAVE_STOP:<br class="gmail_msg">
> +               bt_slave->msg_idx = 0;<br class="gmail_msg">
> +               break;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       default:<br class="gmail_msg">
> +               break;<br class="gmail_msg">
> +       }<br class="gmail_msg">
> +       spin_unlock(&bt_slave->lock);<br class="gmail_msg">
> +<br class="gmail_msg">
> +       return 0;<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +static int bt_i2c_probe(struct i2c_client *client,<br class="gmail_msg">
> +                       const struct i2c_device_id *id)<br class="gmail_msg">
> +{<br class="gmail_msg">
> +       struct bt_i2c_slave *bt_slave;<br class="gmail_msg">
> +       int ret;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       bt_slave = devm_kzalloc(&client->dev, sizeof(struct bt_i2c_slave),<br class="gmail_msg">
> +                               GFP_KERNEL);<br class="gmail_msg">
> +       if (!bt_slave)<br class="gmail_msg">
> +               return -ENOMEM;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       spin_lock_init(&bt_slave->lock);<br class="gmail_msg">
> +       init_waitqueue_head(&bt_slave->wait_queue);<br class="gmail_msg">
> +       atomic_set(&bt_slave->request_queue_len, 0);<br class="gmail_msg">
> +       atomic_set(&bt_slave->response_in_progress, 0);<br class="gmail_msg">
> +       INIT_LIST_HEAD(&bt_slave->request_queue);<br class="gmail_msg">
> +<br class="gmail_msg">
> +       mutex_init(&bt_slave->file_mutex);<br class="gmail_msg">
> +<br class="gmail_msg">
> +       bt_slave->miscdev.minor = MISC_DYNAMIC_MINOR;<br class="gmail_msg">
> +       bt_slave-><a href="http://miscdev.name" rel="noreferrer" class="gmail_msg" target="_blank">miscdev.name</a> = DEVICE_NAME;<br class="gmail_msg">
> +       bt_slave->miscdev.fops = &bt_fops;<br class="gmail_msg">
> +       bt_slave->miscdev.parent = &client->dev;<br class="gmail_msg">
> +       ret = misc_register(&bt_slave->miscdev);<br class="gmail_msg">
> +       if (ret < 0)<br class="gmail_msg">
> +               return ret;<br class="gmail_msg">
> +<br class="gmail_msg">
> +       bt_slave->client = client;<br class="gmail_msg">
> +       i2c_set_clientdata(client, bt_slave);<br class="gmail_msg">
> +       ret = i2c_slave_register(client, bt_i2c_slave_cb);<br class="gmail_msg">
> +       if (ret < 0) {<br class="gmail_msg">
> +               misc_deregister(&bt_slave->miscdev);<br class="gmail_msg">
> +               return ret;<br class="gmail_msg">
> +       }<br class="gmail_msg">
> +<br class="gmail_msg">
> +       return 0;<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +static int bt_i2c_remove(struct i2c_client *client)<br class="gmail_msg">
> +{<br class="gmail_msg">
> +       struct bt_i2c_slave *bt_slave = i2c_get_clientdata(client);<br class="gmail_msg">
> +<br class="gmail_msg">
> +       i2c_slave_unregister(client);<br class="gmail_msg">
> +       misc_deregister(&bt_slave->miscdev);<br class="gmail_msg">
> +       return 0;<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +static const struct i2c_device_id bt_i2c_id[] = {<br class="gmail_msg">
> +       {"bt-i2c-slave", 0},<br class="gmail_msg">
> +       {},<br class="gmail_msg">
> +};<br class="gmail_msg">
> +MODULE_DEVICE_TABLE(i2c, bt_i2c_id);<br class="gmail_msg">
> +<br class="gmail_msg">
> +static struct i2c_driver bt_i2c_driver = {<br class="gmail_msg">
> +       .driver = {<br class="gmail_msg">
> +               .name           = "bt-i2c-slave",<br class="gmail_msg">
> +       },<br class="gmail_msg">
> +       .probe          = bt_i2c_probe,<br class="gmail_msg">
> +       .remove         = bt_i2c_remove,<br class="gmail_msg">
> +       .id_table       = bt_i2c_id,<br class="gmail_msg">
> +};<br class="gmail_msg">
> +module_i2c_driver(bt_i2c_driver);<br class="gmail_msg">
> +<br class="gmail_msg">
> +MODULE_AUTHOR("Brendan Higgins <<a href="mailto:brendanhiggins@google.com" class="gmail_msg" target="_blank">brendanhiggins@google.com</a>>");<br class="gmail_msg">
> +MODULE_DESCRIPTION("IPMI Block Transfer over I2C slave.");<br class="gmail_msg">
> +MODULE_LICENSE("GPL v2");<br class="gmail_msg">
> diff --git a/drivers/misc/bt.h b/drivers/misc/bt.h<br class="gmail_msg">
> new file mode 100644<br class="gmail_msg">
> index 0000000..c0c2881<br class="gmail_msg">
> --- /dev/null<br class="gmail_msg">
> +++ b/drivers/misc/bt.h<br class="gmail_msg">
> @@ -0,0 +1,19 @@<br class="gmail_msg">
> +#ifndef _DRIVERS_MISC_BT_H<br class="gmail_msg">
> +#define _DRIVERS_MISC_BT_H<br class="gmail_msg">
> +<br class="gmail_msg">
> +#include <linux/types.h><br class="gmail_msg">
> +<br class="gmail_msg">
> +struct bt_msg {<br class="gmail_msg">
> +       u8 len;<br class="gmail_msg">
> +       u8 netfn_lun;<br class="gmail_msg">
> +       u8 seq;<br class="gmail_msg">
> +       u8 cmd;<br class="gmail_msg">
> +       u8 payload[252];<br class="gmail_msg">
> +};<br class="gmail_msg">
> +<br class="gmail_msg">
> +static inline u32 bt_msg_len(struct bt_msg *bt_request)<br class="gmail_msg">
> +{<br class="gmail_msg">
> +       return bt_request->len + 1;<br class="gmail_msg">
> +}<br class="gmail_msg">
> +<br class="gmail_msg">
> +#endif /* _DRIVERS_MISC_BT_H */<br class="gmail_msg">
> --<br class="gmail_msg">
> 2.8.0.rc3.226.g39d4020<br class="gmail_msg">
><br class="gmail_msg">
> _______________________________________________<br class="gmail_msg">
> openbmc mailing list<br class="gmail_msg">
> <a href="mailto:openbmc@lists.ozlabs.org" class="gmail_msg" target="_blank">openbmc@lists.ozlabs.org</a><br class="gmail_msg">
> <a href="https://lists.ozlabs.org/listinfo/openbmc" rel="noreferrer" class="gmail_msg" target="_blank">https://lists.ozlabs.org/listinfo/openbmc</a><br class="gmail_msg">
</blockquote></div>