<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Oct 17, 2016 at 10:10 AM, Jaghathiswari Rankappagounder Natarajan <span dir="ltr"><<a href="mailto:jaghu@google.com" target="_blank">jaghu@google.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">The character device driver implements the user-space API for letting<br>
a user write to the display including any conversion methods necessary<br>
to map the user input to a 7-segment display.<br>
<br>
Signed-off-by: Jaghathiswari Rankappagounder Natarajan <<a href="mailto:jaghu@google.com">jaghu@google.com</a>><br>
---<br>
 drivers/misc/Kconfig          |   5 ++<br>
 drivers/misc/Makefile         |   1 +<br>
 drivers/misc/seven_seg_disp.c | 192 ++++++++++++++++++++++++++++++<wbr>++++++++++++<br>
 drivers/misc/seven_seg_gen.h  |  32 +++++++<br>
 4 files changed, 230 insertions(+)<br>
 create mode 100644 drivers/misc/seven_seg_disp.c<br>
 create mode 100644 drivers/misc/seven_seg_gen.h<br>
<br>
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig<br>
index 4617ddc..cf07817 100644<br>
--- a/drivers/misc/Kconfig<br>
+++ b/drivers/misc/Kconfig<br>
@@ -804,6 +804,11 @@ config PANEL_BOOT_MESSAGE<br>
          An empty message will only clear the display at driver init time. Any other<br>
          printf()-formatted message is valid with newline and escape codes.<br>
<br>
+config SEVEN_SEGMENT_DISPLAY<br>
+       tristate "Character driver for seven segment display support"<br>
+       help<br>
+         Character driver for seven segment display support<br>
+<br>
 config ASPEED_BT_IPMI_HOST<br>
        tristate "BT IPMI host driver"<br>
        help<br>
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile<br>
index 724861b..718dc2b 100644<br>
--- a/drivers/misc/Makefile<br>
+++ b/drivers/misc/Makefile<br>
@@ -57,4 +57,5 @@ obj-$(CONFIG_ECHO)            += echo/<br>
 obj-$(CONFIG_VEXPRESS_SYSCFG)  += vexpress-syscfg.o<br>
 obj-$(CONFIG_CXL_BASE)         += cxl/<br>
 obj-$(CONFIG_PANEL)             += panel.o<br>
+obj-$(CONFIG_SEVEN_SEGMENT_<wbr>DISPLAY)    += seven_seg_disp.o<br>
 obj-$(CONFIG_ASPEED_BT_IPMI_<wbr>HOST)      += bt-host.o<br>
diff --git a/drivers/misc/seven_seg_disp.<wbr>c b/drivers/misc/seven_seg_disp.<wbr>c<br>
new file mode 100644<br>
index 0000000..88df4a0<br>
--- /dev/null<br>
+++ b/drivers/misc/seven_seg_disp.<wbr>c<br>
@@ -0,0 +1,192 @@<br>
+/*<br>
+ * Seven segment display character driver<br>
+ * * Copyright (c) 2016 Google, Inc<br>
+ *<br>
+ * * This program is free software; you can redistribute it and/or modify<br>
+ * * it under the terms of the GNU General Public License version 2 as<br>
+ * * published by the Free Software Foundation.<br>
+ *<br>
+ */<br>
+<br>
+#include <linux/module.h><br>
+#include <linux/version.h><br>
+#include <linux/kernel.h><br>
+#include <linux/types.h><br>
+#include <linux/kdev_t.h><br>
+#include <linux/fs.h><br>
+#include <linux/uaccess.h><br>
+#include <linux/ctype.h><br>
+<br>
+#include "seven_seg_gen.h"<br>
+<br>
+#define MAX_DISP_CHAR_SIZE 3<br>
+<br>
+#define LED_DOT 0x01<br>
+<br>
+/*<br>
+ * 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F<br>
+ *  _       _   _       _   _   _   _   _   _       _       _   _<br>
+ * | |   |  _|  _| |_| |_  |_    | |_| |_| |_| |_  |    _| |_  |_<br>
+ * |_|   | |_   _|   |  _| |_|   | |_|   | | | |_| |_  |_| |_  |<br>
+ *<br>
+ * data[7:1] = led[a:g]<br>
+ */<br>
+const u8 seven_seg_bits[] = {<br>
+       0xFC, 0x60, 0xDA, 0xF2, 0x66, 0xB6, 0xBE, 0xE0,<br>
+       0xFE, 0xF6, 0xEE, 0x3E, 0x9C, 0x7A, 0x9E, 0x8E<br>
+       };<br>
+<br>
+/*<br>
+ * 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F<br>
+ *      _       _   _                              _            _<br>
+ *     |   |_  |_| |_  _   _   _   _   _   _   _  |_    _|  _| | |<br>
+ *     |_  |_  |   |                               _|  |_| |_| | |<br>
+ *<br>
+ * data[7:1] = led[a:g]<br>
+ */<br>
+const u8 special_seven_seg_bits[] = {<br>
+       0x00, 0x9C, 0x1E, 0xCE, 0x8E, 0x02, 0x02, 0x02,<br>
+       0x02, 0x02, 0x02, 0x02, 0xB6, 0x7A, 0x7A, 0xEC<br>
+       };<br>
+<br>
+static dev_t seven_seg_devno;<br>
+static struct class *seven_seg_disp_class;<br>
+<br>
+static int seven_seg_disp_open(struct inode *inode, struct file *filp)<br>
+{<br>
+       struct seven_seg_disp_dev *disp_dev;<br>
+<br>
+       disp_dev = container_of(inode->i_cdev,<br>
+                                struct seven_seg_disp_dev, cdev);<br>
+       filp->private_data = disp_dev;<br>
+       return 0;<br>
+}<br>
+<br>
+static int seven_seg_disp_close(struct inode *inode, struct file *filp)<br>
+{<br>
+       filp->private_data = NULL;<br>
+       return 0;<br>
+}<br>
+<br>
+static ssize_t seven_seg_disp_read(struct file *filp, char __user *buf, size_t<br>
+                               len, loff_t *off)<br>
+{<br>
</blockquote><div>May as well return the current value to them.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">+       return 0;<br>
+}<br>
+<br>
+static u16 convert_to_disp_data(char *buf)<br>
+{<br>
+       u8 low_display;<br>
+       u8 high_display;<br>
+       u16 led_value;<br>
+<br>
+       low_display = seven_seg_bits[hex_to_bin(buf[<wbr>2])];<br>
+<br>
+       high_display = (buf[0] == '1') ?<br>
+       special_seven_seg_bits[hex_to_<wbr>bin(buf[1])] :<br>
+       seven_seg_bits[hex_to_bin(buf[<wbr>1])];<br>
+<br>
+       led_value = low_display | (high_display << 8);<br>
+       if (buf[0] == '1') {<br>
+               led_value |= LED_DOT | (LED_DOT << 8);<br>
+       }<br>
+<br>
+       return led_value;<br>
+}<br>
+<br>
+static ssize_t seven_seg_disp_write(struct file *filp, const char __user *buf,<br>
+                               size_t len, loff_t *off)<br>
+{<br>
+       char tmp[MAX_DISP_CHAR_SIZE];<br>
+       int length = len - 1;<br>
+       int i;<br>
+       u8 result;<br>
+<br>
+       struct seven_seg_disp_dev *disp_dev;<br>
+<br>
+       if (length != MAX_DISP_CHAR_SIZE) {<br>
+               return -EINVAL;<br>
+       }<br>
+<br>
+       if (copy_from_user(tmp, buf, length) != 0) {<br>
+               return -EFAULT;<br>
+       }<br>
+<br>
+       for (i = 0; i < MAX_DISP_CHAR_SIZE; i++) {<br></blockquote><div>Only check the bytes actually provided by the user.  If they do a 1-byte write, you'll read uninit data.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+               if (!isxdigit(tmp[i]))<br>
+                       return -EINVAL;<br>
+       }<br>
+<br>
+       result = convert_to_disp_data(tmp);<br></blockquote><div>Pick a more descriptive name than 'result' and 'tmp'.  Describe what the variable contains.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+       disp_dev = filp->private_data;<br>
+       disp_dev->update_seven_seg_<wbr>data(&disp_dev->parent, result);<br>
+<br>
+       return len;<br>
+}<br>
+<br>
+static const struct file_operations seven_seg_disp_fops = {<br>
+<br>
+       .owner = THIS_MODULE,<br>
+       .open = seven_seg_disp_open,<br>
+       .release = seven_seg_disp_close,<br>
+       .read = seven_seg_disp_read,<br>
+       .write = seven_seg_disp_write<br>
+};<br>
+<br>
+void rem_cdev(struct seven_seg_disp_dev *disp_dev)<br>
+{<br>
+       cdev_del(&disp_dev->cdev);<br>
+       device_destroy(seven_seg_disp_<wbr>class, seven_seg_devno);<br>
+}<br>
+<br>
+int setup_cdev(struct device parent,<br>
+       struct seven_seg_disp_dev *disp_dev,<br>
+       void (*update_disp_data)(struct device *, u16 data))<br>
+{<br>
+       struct device *dev;<br>
+       int err;<br>
+<br>
+       dev = device_create(seven_seg_disp_<wbr>class, &parent, seven_seg_devno,<br>
+                       NULL, "seven_seg_disp_val");<br>
+       if (dev == NULL)<br>
+               return -1;<br>
+       disp_dev->parent = parent;<br>
+       disp_dev->dev = dev;<br>
+       disp_dev->update_seven_seg_<wbr>data = update_disp_data;<br>
+       cdev_init(&disp_dev->cdev, &seven_seg_disp_fops);<br>
+       disp_dev->cdev.ops = &seven_seg_disp_fops;<br>
+       err = cdev_add(&disp_dev->cdev, seven_seg_devno, 1);<br>
+       if (err)<br>
+               device_destroy(seven_seg_disp_<wbr>class, seven_seg_devno);<br>
+       return err;<br>
+}<br>
+<br>
+static int __init seven_seg_disp_init(void)<br>
+{<br>
+       if (alloc_chrdev_region(&seven_<wbr>seg_devno, 0, 1, "disp_state") < 0) {<br>
+               return -1;<br>
+       }<br>
+<br>
+       seven_seg_disp_class = class_create(THIS_MODULE, "disp_state");<br>
+       if (seven_seg_disp_class == NULL) {<br>
+               goto unregister;<br>
+               return -1;<br>
+       }<br>
+<br>
+unregister:<br>
+       unregister_chrdev_region(<wbr>seven_seg_devno, 1);<br>
+       return -1;<br>
+}<br>
+<br>
+static void __exit seven_seg_disp_exit(void)<br>
+{<br>
+       class_destroy(seven_seg_disp_<wbr>class);<br>
+       unregister_chrdev_region(<wbr>seven_seg_devno, 1);<br>
+}<br>
+<br>
+module_init(seven_seg_disp_<wbr>init);<br>
+module_exit(seven_seg_disp_<wbr>exit);<br>
+MODULE_LICENSE("GPL");<br>
+MODULE_AUTHOR("Jaghathiswari Rankappagounder Natarajan <<a href="mailto:jaghu@google.com">jaghu@google.com</a>>");<br>
+MODULE_DESCRIPTION("Seven segment display character driver");<br>
diff --git a/drivers/misc/seven_seg_gen.h b/drivers/misc/seven_seg_gen.h<br>
new file mode 100644<br>
index 0000000..5748a18<br>
--- /dev/null<br>
+++ b/drivers/misc/seven_seg_gen.h<br></blockquote><div><br></div><div>Why is this header a different name from the .c?</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
@@ -0,0 +1,32 @@<br>
+/*<br>
+ * Seven segment display support<br>
+ * * Copyright (c) 2016 Google, Inc<br>
+ *<br>
+ * * This program is free software; you can redistribute it and/or modify<br>
+ * * it under the terms of the GNU General Public License version 2 as<br>
+ * * published by the Free Software Foundation.<br>
+ *<br>
+ */<br>
+<br>
+#ifndef SEVEN_SEG_H<br>
+#define SEVEN_SEG_H<br></blockquote><div><br></div><div>Guards don't match header name.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+#include <linux/device.h><br>
+#include <linux/cdev.h><br>
+<br>
+#define DEFAULT_REFRESH_INTERVAL 1000<br></blockquote><div><br></div><div>This doesn't seem to be used by any of the APIs provided.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+struct seven_seg_disp_dev {<br>
+       struct device parent;<br>
+       struct device *dev;<br>
+       struct cdev cdev;<br>
+       void (*update_seven_seg_data)(<wbr>struct device *, u16 data);<br>
+};<br>
+<br>
+int setup_cdev(struct device parent,<br>
+       struct seven_seg_disp_dev *disp_dev,<br>
+       void (*update_disp_data)(struct device *, u16 data));<br></blockquote><div><br></div><div>Way too generic of a method name.  This name will be exposed in any .c that includes this header.  Pick something that describes what it is doing _and_ what subsystem it belongs to.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+void rem_cdev(struct seven_seg_disp_dev *disp_dev);<br>
+<br>
+#endif<br>
--<br>
2.8.0.rc3.226.g39d4020<br>
<br>
</blockquote></div><br></div></div>