Skip to content

Instantly share code, notes, and snippets.

@alyssarosenzweig
Created February 25, 2017 18:12
Show Gist options
  • Save alyssarosenzweig/f14b7bdc279bd726801347e42010998c to your computer and use it in GitHub Desktop.
Save alyssarosenzweig/f14b7bdc279bd726801347e42010998c to your computer and use it in GitHub Desktop.
Prelimininary framebuffer driver for Topaz signature pads
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 1aeb80e52424..c8bd6991339a 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -985,6 +985,12 @@ config HID_ALPS
Say Y here if you have a Alps touchpads over i2c-hid or usbhid
and want support for its special functionalities.
+config HID_TOPAZ
+ tristate "Topaz signature pad HID support"
+ depends on USB_HID
+ ---help---
+ Support for Topaz signature pads that communicate over USB HID.
+
endmenu
endif # HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 4d111f23e801..ee45eb6e70c6 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -94,6 +94,7 @@ obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o
obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o
obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o
obj-$(CONFIG_HID_TIVO) += hid-tivo.o
+obj-$(CONFIG_HID_TOPAZ) += hid-topaz.o
obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o
obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o
obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 86c95d30ac80..ed3fd69bb9c1 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -1119,4 +1119,8 @@
#define USB_VENDOR_ID_UGTIZER 0x2179
#define USB_DEVICE_ID_UGTIZER_TABLET_GP0610 0x0053
+
+#define USB_VENDOR_ID_TOPAZ 0x06A8
+#define USB_DEVICE_ID_TOPAZ_LBK766 0x0043
+
#endif
diff --git a/drivers/hid/hid-topaz.c b/drivers/hid/hid-topaz.c
new file mode 100644
index 000000000000..bb7a0af5ed7a
--- /dev/null
+++ b/drivers/hid/hid-topaz.c
@@ -0,0 +1,239 @@
+/*
+ * HID driver for Topaz signature pads
+ *
+ * Copyright (c) 2017 Alyssa Rosenzweig
+ *
+ * Author: Alyssa Rosenzweig <alyssa@rosenzweig.io>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+
+#include "hid-ids.h"
+
+// names from Topaz API documentation
+
+#define MODE_CLEAR 0
+#define MODE_COMPLEMENT 1
+#define MODE_OPAQUE 2
+#define MODE_TRANSPARENT 3
+
+static int topaz_send(struct hid_device *dev, u8 *packet, size_t sz)
+{
+ int ret;
+
+ u8 *buf = kmemdup(packet, sz, GFP_KERNEL);
+ if(!buf)
+ return -ENOMEM;
+
+ if(!dev->ll_driver->output_report)
+ return -ENODEV;
+
+ ret = hid_hw_output_report(dev, buf, sz);
+ kfree(buf);
+
+ return ret;
+}
+
+static int topaz_blit8(struct hid_device *dev, u16 x, u16 y, void* data)
+{
+ u8 packet[11 + 8] = {0xF2, 0x07, 0x02, 0, 0, 0, 0, 0, 8, 0, 8};
+
+ packet[3] = (x & 0xFF00) >> 8;
+ packet[4] = (x & 0x00FF);
+ packet[5] = (y & 0xFF00) >> 8;
+ packet[6] = (y & 0x00FF);
+
+ memcpy(packet + 11, data, 8);
+
+ return topaz_send(dev, packet, sizeof(packet)) & 0;
+}
+
+static int topaz_bitmap(struct hid_device *dev, u16 xpos, u16 ypos, u16 width, u16 height, u8* data)
+{
+ u8 block[8];
+
+ int ret;
+ int x, y, row;
+
+ int awidth = ((width + 7) >> 3) << 3;
+
+ for (x = 0; x < width; x += 8) {
+ for (y = 0; y < height; y += 8) {
+ /* blit 8x8 block */
+
+ memset(block, 0, sizeof(block));
+ for (row = 0; row < 8 && y + row < height; ++row) {
+ block[row] = data[((y + row) * awidth + x) >> 3];
+ }
+
+ ret = topaz_blit8(dev, xpos + x, ypos + y, block);
+
+ if(ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int topaz_rectangle(struct hid_device *dev, u16 x, u16 y, u16 w, u16 h, u8 mode)
+{
+ u8 packet[11] = { 0xFF, 0x12, mode };
+
+ packet[3] = (x & 0xFF00) >> 8;
+ packet[4] = (x & 0x00FF);
+ packet[5] = (y & 0xFF00) >> 8;
+ packet[6] = (y & 0x00FF);
+ packet[7] = (w & 0xFF00) >> 8;
+ packet[8] = (w & 0x00FF);
+ packet[9] = (h & 0xFF00) >> 8;
+ packet[10] = (h & 0x00FF);
+
+ return topaz_send(dev, packet, sizeof(packet));
+}
+
+static int topaz_clear(struct hid_device *dev)
+{
+ return topaz_rectangle(dev, 0, 0, 320, 240, 2);
+}
+
+static int topaz_backlight(struct hid_device *dev, bool on)
+{
+ u8 packet[] = { 0x81, 0x02 | (!on) };
+ return topaz_send(dev, packet, sizeof(packet));
+}
+
+static int topazfb_probe(struct hid_device *dev);
+
+static int topaz_probe(struct hid_device *dev, const struct hid_device_id *id)
+{
+ int ret;
+
+ ret = hid_parse(dev);
+ if(ret)
+ goto done;
+
+ ret = hid_hw_start(dev, HID_CONNECT_DEFAULT);
+ if(ret)
+ goto done;
+
+ ret = hid_hw_open(dev);
+ if(ret)
+ goto done;
+
+ ret = topaz_clear(dev);
+ //if(ret)
+ // goto done;
+
+ ret = topazfb_probe(dev);
+ if(ret)
+ goto done;
+done:
+ return ret;
+}
+static void topaz_remove(struct hid_device *dev)
+{
+ hid_hw_stop(dev);
+}
+
+static struct fb_fix_screeninfo topazfb_fix = {
+ .id = "topaz",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_MONO01,
+ .xpanstep = 0,
+ .ypanstep = 0,
+ .ywrapstep = 0,
+ .accel = FB_ACCEL_NONE
+};
+
+struct topazfb_par {
+ struct hid_device *hid;
+};
+
+static void topazfb_imageblit(struct fb_info *p, const struct fb_image *image)
+{
+ if(image->depth != 1) {
+ printk("Bad image depth %d\n", image->depth);
+ return;
+ }
+
+ topaz_bitmap(((struct topazfb_par*) p->par)->hid,
+ image->dx, image->dy,
+ image->width, image->height,
+ image->data);
+}
+
+static void topazfb_fillrect(struct fb_info *p, const struct fb_fillrect *region)
+{
+ int mode = region->rop == ROP_XOR ? MODE_COMPLEMENT : MODE_OPAQUE;
+
+ topaz_rectangle(((struct topazfb_par*) p->par)->hid,
+ region->dx, region->dy,
+ region->width, region->height,
+ mode);
+}
+
+static struct fb_ops topazfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_fillrect = topazfb_fillrect,
+ .fb_imageblit = topazfb_imageblit
+};
+
+static int topazfb_probe(struct hid_device *dev)
+{
+ struct fb_info *info;
+
+ info = framebuffer_alloc(sizeof(struct topazfb_par), NULL);
+
+ struct topazfb_par *par = info->par;
+ par->hid = dev;
+
+ info->fbops = &topazfb_ops;
+ info->fix = topazfb_fix;
+ info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_IMAGEBLIT;
+ info->screen_base = kmalloc(320*240>>3, GFP_KERNEL);
+
+ info->var.xres = 320;
+ info->var.yres = 240;
+
+ if(register_framebuffer(info) < 0) {
+ return -EINVAL;
+ }
+
+ return 0;
+
+}
+
+static const struct hid_device_id topaz_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_TOPAZ, USB_DEVICE_ID_TOPAZ_LBK766) },
+ { }
+};
+
+MODULE_DEVICE_TABLE(hid, topaz_devices);
+
+static struct hid_driver topaz_driver = {
+ .name = "topaz",
+ .id_table = topaz_devices,
+ .probe = topaz_probe,
+ .remove = topaz_remove
+};
+
+module_hid_driver(topaz_driver);
+
+MODULE_AUTHOR("Alyssa Rosenzweig <alyssa@rosenzweig.io>");
+MODULE_DESCRIPTION("HID driver for Topaz signature pads");
+MODULE_LICENSE("GPL");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment