diff mbox

Driver for Bosto 14WA graphics tablet

Message ID CAGFQMzQsgMYDwZrq7B1_PNohd7wY6bd5zybB_URKTDJss7gNXQ@mail.gmail.com (mailing list archive)
State Changes Requested
Headers show

Commit Message

Leslie Viljoen Oct. 4, 2015, 6:51 a.m. UTC
Add support for the Bosto 14WA graphics tablet. This is originally based
off the hanwang.c tablet driver since the chip is similar.

I added what I think are the right entries to quirks to make HID ignore
the tablet - so that this driver can load. I'm still working on getting a
proper kernel dev VM so I've not tested that part. But the change is
trivial.

checkpatch.pl said I may need to edit MAINTAINERS because of the
new file but I don't see anything about that in the SubmittingPatches
doc. Do I need to do that?

I'm a novice, apologies for any mistakes.

Signed-off-by: Leslie Viljoen <leslieviljoen@gmail.com>

---
 drivers/hid/hid-ids.h             |   1 +
 drivers/hid/usbhid/hid-quirks.c   |   2 +
 drivers/input/tablet/bosto_14wa.c | 555 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 558 insertions(+)
 create mode 100644 drivers/input/tablet/bosto_14wa.c

+}
+
+module_init(bosto_init);
+module_exit(bosto_exit);

Comments

Benjamin Tissoires Oct. 5, 2015, 9:19 a.m. UTC | #1
Hi Leslie,

thanks for your submission.

As a foreword, I must say that you want to review your sending
process. Your email client stripped all the spaces/tabs and we can not
accept such a patch. I would encourage you to directly use the command
"git send-email" to send patches.

Little bit more inlined:

On Sun, Oct 4, 2015 at 8:51 AM, Leslie Viljoen <leslieviljoen@gmail.com> wrote:
> Add support for the Bosto 14WA graphics tablet. This is originally based
> off the hanwang.c tablet driver since the chip is similar.
>
> I added what I think are the right entries to quirks to make HID ignore
> the tablet - so that this driver can load. I'm still working on getting a
> proper kernel dev VM so I've not tested that part. But the change is
> trivial.
>
> checkpatch.pl said I may need to edit MAINTAINERS because of the
> new file but I don't see anything about that in the SubmittingPatches
> doc. Do I need to do that?

Ideally you do. This is mainly to receive patches from others when
they are sending patches against your driver. This way, you can be
sure not to break your current device. On a day to day basis, we do
not update it as often as we should, and this is a little bit of a
shame :)

>
> I'm a novice, apologies for any mistakes.
>
> Signed-off-by: Leslie Viljoen <leslieviljoen@gmail.com>
>
> ---
>  drivers/hid/hid-ids.h             |   1 +
>  drivers/hid/usbhid/hid-quirks.c   |   2 +
>  drivers/input/tablet/bosto_14wa.c | 555 ++++++++++++++++++++++++++++++++++++++

Is there any reasons not to use a hid driver directly?

From what I can read of the code, it looks like your device is
directly sending the correct events, you just need a special way of
parsing them.

There are several advantages not to write a full usb driver: you only
focus on the parsing of the events, you do not need to handle
suspend/resume, you can use your driver on older kernels with some
good looking udev rules and a sideload of your driver.

drivers/hid/hid-primax.c is a driver with just a raw_event processing,
and you might want to use it as a basis.

Cheers,
Benjamin

>  3 files changed, 558 insertions(+)
>  create mode 100644 drivers/input/tablet/bosto_14wa.c
>
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index f769208..77ec24a 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -450,6 +450,7 @@
>  #define USB_VENDOR_ID_HANWANG 0x0b57
>  #define USB_DEVICE_ID_HANWANG_TABLET_FIRST 0x5000
>  #define USB_DEVICE_ID_HANWANG_TABLET_LAST 0x8fff
> +#define USB_DEVICE_ID_BOSTO14WA_2 0x9018
>
>  #define USB_VENDOR_ID_HANVON 0x20b3
>  #define USB_DEVICE_ID_HANVON_MULTITOUCH 0x0a18
> diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
> index 1dff8f0..840b5d7 100644
> --- a/drivers/hid/usbhid/hid-quirks.c
> +++ b/drivers/hid/usbhid/hid-quirks.c
> @@ -132,6 +132,8 @@ static const struct hid_blacklist {
>
>   { USB_VENDOR_ID_PI_ENGINEERING,
> USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL,
> HID_QUIRK_HIDINPUT_FORCE },
>
> + { USB_VENDOR_ID_HANWANG, USB_DEVICE_ID_BOSTO14WA_2, HID_QUIRK_IGNORE },
> +
>   { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_MULTI_TOUCH,
> HID_QUIRK_MULTI_INPUT },
>   { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS,
> HID_QUIRK_MULTI_INPUT },
>   { USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD,
> HID_QUIRK_NO_INIT_REPORTS },
> diff --git a/drivers/input/tablet/bosto_14wa.c
> b/drivers/input/tablet/bosto_14wa.c
> new file mode 100644
> index 0000000..64f1d71
> --- /dev/null
> +++ b/drivers/input/tablet/bosto_14wa.c
> @@ -0,0 +1,555 @@
> +/*
> + *  USB Bosto tablet support
> + *
> + *  Original Copyright (c) 2010 Xing Wei <weixing@hanwang.com.cn>
> + *
> + */
> +
> +/*
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +#include <linux/jiffies.h>
> +#include <linux/stat.h>
> +#include <linux/types.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/usb/input.h>
> +#include <linux/fcntl.h>
> +#include <linux/unistd.h>
> +
> +#define DRIVER_DESC     "USB Bosto(2nd Gen) tablet driver"
> +#define DRIVER_LICENSE  "GPL"
> +
> +MODULE_AUTHOR("Xing Wei <weixing@hanwang.com.cn>");
> +MODULE_AUTHOR("Aidan Walton <aidan@wires3.net>");
> +MODULE_AUTHOR("Leslie Viljoen <leslieviljoen@gmail.com>");
> +MODULE_AUTHOR("Tomasz Flis <tflis84@gmail.com>");
> +
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_LICENSE(DRIVER_LICENSE);
> +
> +#define USB_VENDOR_ID_HANWANG 0x0b57
> +#define USB_DEVICE_ID_BOSTO14WA_2 0x9018
> +
> +#define BOSTO_TABLET_INT_CLASS     0x0003
> +#define BOSTO_TABLET_INT_SUB_CLASS 0x0001
> +#define BOSTO_TABLET_INT_PROTOCOL  0x0002
> +
> +/* Delay between TOOL_IN event and first reported pressure > 0 (in ms).
> + * Used to suppress settle time for pen ABS positions.
> + */
> +#define PEN_WRITE_DELAY  230
> +#define PKGLEN_MAX        10
> +#define MAX_DEVICE_NAME   64
> +#define MAX_PHYS_ADDRESS  32
> +#define NO_ERROR           0
> +
> +/* device IDs */
> +#define STYLUS_DEVICE_ID 0x02
> +#define TOUCH_DEVICE_ID  0x03
> +#define CURSOR_DEVICE_ID 0x06
> +#define ERASER_DEVICE_ID 0x0A
> +#define PAD_DEVICE_ID    0x0F
> +
> +enum bosto_tablet_type {
> + BOSTO_14WA
> +};
> +
> +struct bosto {
> + unsigned char *data;
> + dma_addr_t data_dma;
> + struct input_dev *stylus;
> + struct input_dev *eraser;
> + struct usb_device *usbdev;
> + struct urb *urb0;
> + struct urb *urb1;
> + const struct bosto_features *features;
> + unsigned int current_tool;
> + unsigned int current_id;
> + char stylus_name[MAX_DEVICE_NAME];
> + char eraser_name[MAX_DEVICE_NAME];
> + char phys[MAX_PHYS_ADDRESS];
> +};
> +
> +struct bosto_features {
> + unsigned short pid;
> + char *name;
> + enum bosto_tablet_type type;
> + int pkg_len;
> + int max_x;
> + int max_y;
> + int max_tilt_x;
> + int max_tilt_y;
> + int max_pressure;
> +};
> +
> +static const struct bosto_features features_array[] = {
> + { USB_DEVICE_ID_BOSTO14WA_2, "Bosto Kingtee 14WA", BOSTO_14WA, PKGLEN_MAX,
> + 0x27de, 0x1cfe, 0x3f, 0x7f, 2048 },
> +};
> +
> +static const int hw_eventtypes[] = {
> + EV_KEY, EV_ABS, EV_MSC,
> +};
> +
> +static const int hw_absevents[] = {
> + ABS_X, ABS_Y, ABS_PRESSURE, ABS_MISC
> +};
> +
> +static const int hw_btnevents[] = {
> + BTN_DIGI, BTN_TOUCH, BTN_STYLUS, BTN_STYLUS2, BTN_TOOL_PEN,
> + BTN_TOOL_BRUSH, BTN_TOOL_RUBBER, BTN_TOOL_PENCIL, BTN_TOOL_AIRBRUSH,
> + BTN_TOOL_FINGER, BTN_TOOL_MOUSE
> +};
> +
> +static const int hw_mscevents[] = {
> + MSC_SERIAL,
> +};
> +
> +static struct input_dev *get_current_input(struct bosto *bosto)
> +{
> + return (bosto->current_id == ERASER_DEVICE_ID) ? bosto->eraser :
> bosto->stylus;
> +}
> +
> +static void bosto_tool_out(struct bosto *bosto)
> +{
> + struct input_dev *input_dev;
> +
> + input_dev = get_current_input(bosto);
> + input_report_key(input_dev, bosto->current_tool, 0);
> + bosto->current_id = 0;
> +}
> +
> +static void bosto_tool_in(struct bosto *bosto, unsigned long *stamp,
> u8 pen_end)
> +{
> + unsigned int id, tool;
> + struct input_dev *input_dev;
> +
> + /* Time stamp the 'TOOL IN' event and add delay. */
> + *stamp = jiffies + PEN_WRITE_DELAY * HZ / 1000;
> +
> + switch (pen_end & 0xf0) {
> + /* Stylus Tip in prox */
> + case 0x20:
> + id = STYLUS_DEVICE_ID;
> + tool = BTN_TOOL_PEN;
> + break;
> +
> + /* Stylus Eraser in prox */
> + case 0xa0:
> + id = ERASER_DEVICE_ID;
> + tool = BTN_TOOL_RUBBER;
> + break;
> +
> + default:
> + id = 0;
> + tool = BTN_TOOL_PEN;
> + break;
> + }
> +
> + bosto->current_id = id;
> + bosto->current_tool = tool;
> + input_dev = get_current_input(bosto);
> + input_report_abs(input_dev, ABS_MISC, id);
> + input_report_key(input_dev, tool, 1);
> +}
> +
> +static void bosto_pen_float(struct bosto *bosto, u16 *p, u16 *x, u16 *y,
> + u8 *data)
> +{
> + struct input_dev *input_dev;
> + *x = (data[1] << 8) | data[2];      /* Set x ABS */
> + *y = (data[3] << 8) | data[4];      /* Set y ABS */
> + *p = 0;
> +
> + input_dev = get_current_input(bosto);
> + input_report_key(input_dev, bosto->current_tool, 1);
> + input_report_key(input_dev, BTN_TOUCH, 0);
> +
> + switch (data[0]) {
> + case 0xa0 ... 0xa1:
> + input_report_key(input_dev, BTN_STYLUS, 0);
> + break;
> + case 0xa2 ... 0xa3:
> + input_report_key(input_dev, BTN_STYLUS, 1);
> + break;
> + }
> +}
> +
> +static void bosto_pen_contact(struct bosto *bosto, u16 *p, u16 *x, u16 *y,
> + unsigned long stamp, u8 *data)
> +{
> + struct input_dev *input_dev;
> +
> + /*
> + * All a little strange; these 4 bytes are always seen whenever the pen
> + * is in contact with the tablet. 'e0 + e1', without the stylus button
> + * pressed and 'e2 + e3' with the stylus button pressed. Either of the
> + * buttons. In either case the byte value jitters between a pair of
> + * either of the two states dependent on the button press.
> + */
> +
> + input_dev = get_current_input(bosto);
> + input_report_key(input_dev, bosto->current_tool, 1);
> + input_report_key(input_dev, BTN_TOUCH, 1);
> +
> + *x = (data[1] << 8) | data[2];      /* Set x ABS */
> + *y = (data[3] << 8) | data[4];      /* Set y ABS */
> +
> + /*
> + * Set 2048 Level pressure sensitivity.
> + * NOTE: The pen button magnifies the pressure sensitivity. Bring
> + * the pen in with the button pressed, ignore the right click response
> + * and keep the button held down. Enjoy the pressure magnification.
> + */
> +
> + if (time_after(jiffies, stamp))
> + *p = (data[5] << 3) | ((data[6] & 0xc0) >> 5);
> + else
> + *p = 0;
> +
> + switch (data[0]) {
> + case 0xe0 ... 0xe1:
> + input_report_key(input_dev, BTN_STYLUS, 0);  /* no stylus btn */
> + break;
> + case 0xe2 ... 0xe3:
> + input_report_key(input_dev, BTN_STYLUS, 1);  /* stylus btn */
> + break;
> + }
> +}
> +
> +
> +static void bosto_parse_packet(struct bosto *bosto)
> +{
> + unsigned char *data = bosto->data;
> + struct input_dev *input_dev;
> + struct usb_device *dev = bosto->usbdev;
> + u16 x = 0;
> + u16 y = 0;
> + u16 p = 0;
> + static unsigned long stamp;
> +
> + dev_dbg(&dev->dev,
> + "Bosto packet: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
> + data[0], data[1], data[2], data[3], data[4], data[5], data[6],
> + data[7], data[8], data[9]);
> +
> + switch (data[0]) {
> + /* pen event */
> + case 0x02:
> + switch (data[1]) {
> + case 0x80:
> + bosto_tool_out(bosto);
> + dev_dbg(&dev->dev, "TOOL OUT. PEN ID:Tool %x:%x\n",
> + bosto->current_id, bosto->current_tool);
> + break;
> +
> + case 0xc2:
> + bosto_tool_in(bosto, &stamp, data[3]);
> + dev_dbg(&dev->dev, "TOOL IN: ID:Tool %x:%x\n",
> + bosto->current_id, bosto->current_tool);
> + break;
> +
> + /* Pen trackable but not in contact with screen */
> + case 0xa0 ... 0xa3:
> + bosto_pen_float(bosto, &p, &x, &y, &data[1]);
> + dev_dbg(&dev->dev, "PEN FLOAT: ID:Tool %x:%x\n",
> + bosto->current_id, bosto->current_tool);
> + break;
> +
> + /* Pen contact */
> + case 0xe0 ... 0xe3:
> + bosto_pen_contact(bosto, &p, &x, &y, stamp, &data[1]);
> + dev_dbg(&dev->dev, "PEN TOUCH: ID:Tool %x:%x\n",
> + bosto->current_id, bosto->current_tool);
> + break;
> + }
> + break;
> +
> + case 0x0c:
> + dev_dbg(&dev->dev,
> + "Tablet Event. Packet data[0]: %02x\n", data[0]);
> + input_dev = get_current_input(bosto);
> + input_report_abs(input_dev, ABS_MISC, bosto->current_id);
> + input_event(input_dev, EV_MSC, MSC_SERIAL, bosto->features->pid);
> + input_sync(input_dev);
> + break;
> +
> + default:
> + dev_dbg(&dev->dev,
> + "Error packet. Packet data[0]:  %02x\n", data[0]);
> + break;
> + }
> +
> + input_dev = get_current_input(bosto);
> + input_report_abs(input_dev, ABS_X, le16_to_cpup((__le16 *)&x));
> + input_report_abs(input_dev, ABS_Y, le16_to_cpup((__le16 *)&y));
> + input_report_abs(input_dev, ABS_PRESSURE, p);
> + input_event(input_dev, EV_MSC, MSC_SERIAL, bosto->features->pid);
> +
> + input_sync(bosto->stylus);
> + input_sync(bosto->eraser);
> +}
> +
> +static void bosto_irq(struct urb *urb)
> +{
> + struct bosto *bosto = urb->context;
> + struct usb_device *dev = bosto->usbdev;
> + int retval;
> +
> + switch (urb->status) {
> + case 0:
> + /* success */;
> + bosto_parse_packet(bosto);
> + break;
> + case -ECONNRESET:
> + case -ENOENT:
> + case -ESHUTDOWN:
> + /* this urb is terminated, clean up */
> + dev_err(&dev->dev, "%s - urb shutting down with status: %d",
> + __func__, urb->status);
> + return;
> + default:
> + dev_err(&dev->dev, "%s - nonzero urb status received: %d",
> + __func__, urb->status);
> + break;
> + }
> +
> + retval = usb_submit_urb(urb, GFP_ATOMIC);
> + if (retval)
> + dev_err(&dev->dev, "%s - usb_submit_urb failed with result %d",
> + __func__, retval);
> +}
> +
> +static int bosto_stylus_open(struct input_dev *dev)
> +{
> + struct bosto *bosto = input_get_drvdata(dev);
> +
> + bosto->urb0->dev = bosto->usbdev;
> + if (usb_submit_urb(bosto->urb0, GFP_KERNEL))
> + return -EIO;
> +
> + return 0;
> +}
> +
> +static void bosto_stylus_close(struct input_dev *dev)
> +{
> + struct bosto *bosto = input_get_drvdata(dev);
> +
> + usb_kill_urb(bosto->urb0);
> +}
> +
> +static int bosto_eraser_open(struct input_dev *dev)
> +{
> + struct bosto *bosto = input_get_drvdata(dev);
> +
> + bosto->urb1->dev = bosto->usbdev;
> + if (usb_submit_urb(bosto->urb1, GFP_KERNEL))
> + return -EIO;
> +
> + return 0;
> +}
> +
> +static void bosto_eraser_close(struct input_dev *dev)
> +{
> + struct bosto *bosto = input_get_drvdata(dev);
> +
> + usb_kill_urb(bosto->urb1);
> +}
> +
> +static bool get_features(struct usb_device *dev, struct bosto *bosto)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(features_array); i++) {
> + if (le16_to_cpu(dev->descriptor.idProduct) == features_array[i].pid) {
> + bosto->features = &features_array[i];
> + return true;
> + }
> + }
> +
> + return false;
> +}
> +
> +static int bosto_create_input_device(struct usb_interface *intf,
> + struct usb_device *dev, struct bosto *bosto, struct urb **urb,
> + struct input_dev **device, const char *device_name,
> + int (*open)(struct input_dev *), void (*close)(struct input_dev *))
> +{
> + int i;
> + int status = NO_ERROR;
> + struct usb_endpoint_descriptor *endpoint;
> +
> + *urb = usb_alloc_urb(0, GFP_KERNEL);
> + if (*urb) {
> + endpoint = &intf->cur_altsetting->endpoint[0].desc;
> + usb_fill_int_urb(*urb, dev, usb_rcvintpipe(dev,
> + endpoint->bEndpointAddress), bosto->data,
> + bosto->features->pkg_len, bosto_irq, bosto,
> + endpoint->bInterval);
> +
> + (*urb)->transfer_dma = bosto->data_dma;
> + (*urb)->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
> +
> + *device = input_allocate_device();
> + if (*device) {
> + usb_make_path(dev, bosto->phys, sizeof(bosto->phys));
> + strlcat(bosto->phys, "/input0", sizeof(bosto->phys));
> + (*device)->phys = bosto->phys;
> +
> + (*device)->name = device_name;
> + usb_to_input_id(dev, &(*device)->id);
> +
> + (*device)->dev.parent = &intf->dev;
> +
> + input_set_drvdata(*device, bosto);
> +
> + (*device)->open  = open;
> + (*device)->close = close;
> +
> + for (i = 0; i < ARRAY_SIZE(hw_eventtypes); ++i)
> + __set_bit(hw_eventtypes[i], (*device)->evbit);
> +
> + for (i = 0; i < ARRAY_SIZE(hw_absevents); ++i)
> + __set_bit(hw_absevents[i], (*device)->absbit);
> +
> + for (i = 0; i < ARRAY_SIZE(hw_btnevents); ++i)
> + __set_bit(hw_btnevents[i], (*device)->keybit);
> +
> + for (i = 0; i < ARRAY_SIZE(hw_mscevents); ++i)
> + __set_bit(hw_mscevents[i], (*device)->mscbit);
> +
> + input_set_abs_params(*device, ABS_X, 0,
> + bosto->features->max_x, 0, 0);
> + input_set_abs_params(*device, ABS_Y, 0,
> + bosto->features->max_y, 0, 0);
> + input_set_abs_params(*device, ABS_PRESSURE, 0,
> + bosto->features->max_pressure, 0, 0);
> +
> + status = input_register_device(*device);
> + if (status != NO_ERROR) {
> + input_free_device(*device);
> + usb_free_urb(*urb);
> + }
> + } else {
> + usb_free_urb(*urb);
> + status = -ENOMEM;
> + }
> + } else {
> + status = -ENOMEM;
> + }
> + return status;
> +}
> +
> +static int bosto_probe(struct usb_interface *intf, const struct
> usb_device_id *id)
> +{
> + struct bosto *bosto;
> + int status = NO_ERROR;
> + struct usb_device *dev = interface_to_usbdev(intf);
> +
> + dev_info(&dev->dev, "Bosto_Probe checking Tablet\n");
> +
> + bosto = kzalloc(sizeof(struct bosto), GFP_KERNEL);
> +
> + if (bosto && get_features(dev, bosto)) {
> + bosto->data = usb_alloc_coherent(dev, bosto->features->pkg_len,
> + GFP_KERNEL, &bosto->data_dma);
> +
> + if (bosto->data != NULL) {
> + bosto->usbdev = dev;
> + strlcpy(bosto->stylus_name, bosto->features->name,
> + sizeof(bosto->stylus_name));
> + strlcat(bosto->stylus_name, " stylus",
> + sizeof(bosto->stylus_name));
> + strlcpy(bosto->eraser_name, bosto->features->name,
> + sizeof(bosto->eraser_name));
> + strlcat(bosto->eraser_name, " eraser",
> + sizeof(bosto->eraser_name));
> +
> + status = bosto_create_input_device(intf, dev, bosto,
> + &bosto->urb0, &bosto->stylus,
> + bosto->stylus_name, bosto_stylus_open,
> + bosto_stylus_close);
> +
> + if (status == NO_ERROR) {
> + status = bosto_create_input_device(intf, dev,
> + bosto, &bosto->urb1, &bosto->eraser,
> + bosto->eraser_name, bosto_eraser_open,
> + bosto_eraser_close);
> + }
> +
> + if (status == NO_ERROR) {
> + usb_set_intfdata(intf, bosto);
> + return status;
> + }
> +
> + usb_free_coherent(dev, bosto->features->pkg_len,
> + bosto->data, bosto->data_dma);
> + }
> +
> + } else {
> + status = -ENOMEM;
> + }
> +
> + kfree(bosto);
> + return status;
> +}
> +
> +static void bosto_disconnect(struct usb_interface *intf)
> +{
> + struct bosto *bosto = usb_get_intfdata(intf);
> +
> + input_unregister_device(bosto->stylus);
> + input_unregister_device(bosto->eraser);
> + usb_free_urb(bosto->urb1);
> + usb_free_urb(bosto->urb0);
> + usb_free_coherent(interface_to_usbdev(intf), bosto->features->pkg_len,
> + bosto->data, bosto->data_dma);
> + kfree(bosto);
> + usb_set_intfdata(intf, NULL);
> +}
> +
> +static const struct usb_device_id bosto_ids[] = {
> + { USB_DEVICE_AND_INTERFACE_INFO(
> + USB_VENDOR_ID_HANWANG,
> + USB_DEVICE_ID_BOSTO14WA_2,
> + BOSTO_TABLET_INT_CLASS,
> + BOSTO_TABLET_INT_SUB_CLASS,
> + BOSTO_TABLET_INT_PROTOCOL) },
> + {}
> +};
> +
> +
> +MODULE_DEVICE_TABLE(usb, bosto_ids);
> +
> +static struct usb_driver bosto_driver = {
> + .name       = "bosto_14wa",
> + .probe      = bosto_probe,
> + .disconnect = bosto_disconnect,
> + .id_table   = bosto_ids,
> +};
> +
> +static int __init bosto_init(void)
> +{
> + printk(KERN_INFO "Bosto 2nd Generation USB Driver module being
> initialised\n");
> + return usb_register(&bosto_driver);
> +}
> +
> +static void __exit bosto_exit(void)
> +{
> + usb_deregister(&bosto_driver);
> +}
> +
> +module_init(bosto_init);
> +module_exit(bosto_exit);
> --
> 1.9.1
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Leslie Viljoen Oct. 5, 2015, 10:12 p.m. UTC | #2
On Mon, Oct 5, 2015 at 10:19 PM, Benjamin Tissoires
<benjamin.tissoires@gmail.com> wrote:
> accept such a patch. I would encourage you to directly use the command
> "git send-email" to send patches.

Great, I didn't realise this existed.

> Ideally you do. This is mainly to receive patches from others when
> they are sending patches against your driver. This way, you can be
> sure not to break your current device. On a day to day basis, we do
> not update it as often as we should, and this is a little bit of a
> shame :)

Ok, I will update it.

> drivers/hid/hid-primax.c is a driver with just a raw_event processing,
> and you might want to use it as a basis.

Thanks for the feedback, I'll take a look at that! So far I'd just been
extrapolating from the Hanwang driver and was not aware of other
possibilities!
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index f769208..77ec24a 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -450,6 +450,7 @@ 
 #define USB_VENDOR_ID_HANWANG 0x0b57
 #define USB_DEVICE_ID_HANWANG_TABLET_FIRST 0x5000
 #define USB_DEVICE_ID_HANWANG_TABLET_LAST 0x8fff
+#define USB_DEVICE_ID_BOSTO14WA_2 0x9018

 #define USB_VENDOR_ID_HANVON 0x20b3
 #define USB_DEVICE_ID_HANVON_MULTITOUCH 0x0a18
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 1dff8f0..840b5d7 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -132,6 +132,8 @@  static const struct hid_blacklist {

  { USB_VENDOR_ID_PI_ENGINEERING,
USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL,
HID_QUIRK_HIDINPUT_FORCE },

+ { USB_VENDOR_ID_HANWANG, USB_DEVICE_ID_BOSTO14WA_2, HID_QUIRK_IGNORE },
+
  { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_MULTI_TOUCH,
HID_QUIRK_MULTI_INPUT },
  { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS,
HID_QUIRK_MULTI_INPUT },
  { USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD,
HID_QUIRK_NO_INIT_REPORTS },
diff --git a/drivers/input/tablet/bosto_14wa.c
b/drivers/input/tablet/bosto_14wa.c
new file mode 100644
index 0000000..64f1d71
--- /dev/null
+++ b/drivers/input/tablet/bosto_14wa.c
@@ -0,0 +1,555 @@ 
+/*
+ *  USB Bosto tablet support
+ *
+ *  Original Copyright (c) 2010 Xing Wei <weixing@hanwang.com.cn>
+ *
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/jiffies.h>
+#include <linux/stat.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb/input.h>
+#include <linux/fcntl.h>
+#include <linux/unistd.h>
+
+#define DRIVER_DESC     "USB Bosto(2nd Gen) tablet driver"
+#define DRIVER_LICENSE  "GPL"
+
+MODULE_AUTHOR("Xing Wei <weixing@hanwang.com.cn>");
+MODULE_AUTHOR("Aidan Walton <aidan@wires3.net>");
+MODULE_AUTHOR("Leslie Viljoen <leslieviljoen@gmail.com>");
+MODULE_AUTHOR("Tomasz Flis <tflis84@gmail.com>");
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_HANWANG 0x0b57
+#define USB_DEVICE_ID_BOSTO14WA_2 0x9018
+
+#define BOSTO_TABLET_INT_CLASS     0x0003
+#define BOSTO_TABLET_INT_SUB_CLASS 0x0001
+#define BOSTO_TABLET_INT_PROTOCOL  0x0002
+
+/* Delay between TOOL_IN event and first reported pressure > 0 (in ms).
+ * Used to suppress settle time for pen ABS positions.
+ */
+#define PEN_WRITE_DELAY  230
+#define PKGLEN_MAX        10
+#define MAX_DEVICE_NAME   64
+#define MAX_PHYS_ADDRESS  32
+#define NO_ERROR           0
+
+/* device IDs */
+#define STYLUS_DEVICE_ID 0x02
+#define TOUCH_DEVICE_ID  0x03
+#define CURSOR_DEVICE_ID 0x06
+#define ERASER_DEVICE_ID 0x0A
+#define PAD_DEVICE_ID    0x0F
+
+enum bosto_tablet_type {
+ BOSTO_14WA
+};
+
+struct bosto {
+ unsigned char *data;
+ dma_addr_t data_dma;
+ struct input_dev *stylus;
+ struct input_dev *eraser;
+ struct usb_device *usbdev;
+ struct urb *urb0;
+ struct urb *urb1;
+ const struct bosto_features *features;
+ unsigned int current_tool;
+ unsigned int current_id;
+ char stylus_name[MAX_DEVICE_NAME];
+ char eraser_name[MAX_DEVICE_NAME];
+ char phys[MAX_PHYS_ADDRESS];
+};
+
+struct bosto_features {
+ unsigned short pid;
+ char *name;
+ enum bosto_tablet_type type;
+ int pkg_len;
+ int max_x;
+ int max_y;
+ int max_tilt_x;
+ int max_tilt_y;
+ int max_pressure;
+};
+
+static const struct bosto_features features_array[] = {
+ { USB_DEVICE_ID_BOSTO14WA_2, "Bosto Kingtee 14WA", BOSTO_14WA, PKGLEN_MAX,
+ 0x27de, 0x1cfe, 0x3f, 0x7f, 2048 },
+};
+
+static const int hw_eventtypes[] = {
+ EV_KEY, EV_ABS, EV_MSC,
+};
+
+static const int hw_absevents[] = {
+ ABS_X, ABS_Y, ABS_PRESSURE, ABS_MISC
+};
+
+static const int hw_btnevents[] = {
+ BTN_DIGI, BTN_TOUCH, BTN_STYLUS, BTN_STYLUS2, BTN_TOOL_PEN,
+ BTN_TOOL_BRUSH, BTN_TOOL_RUBBER, BTN_TOOL_PENCIL, BTN_TOOL_AIRBRUSH,
+ BTN_TOOL_FINGER, BTN_TOOL_MOUSE
+};
+
+static const int hw_mscevents[] = {
+ MSC_SERIAL,
+};
+
+static struct input_dev *get_current_input(struct bosto *bosto)
+{
+ return (bosto->current_id == ERASER_DEVICE_ID) ? bosto->eraser :
bosto->stylus;
+}
+
+static void bosto_tool_out(struct bosto *bosto)
+{
+ struct input_dev *input_dev;
+
+ input_dev = get_current_input(bosto);
+ input_report_key(input_dev, bosto->current_tool, 0);
+ bosto->current_id = 0;
+}
+
+static void bosto_tool_in(struct bosto *bosto, unsigned long *stamp,
u8 pen_end)
+{
+ unsigned int id, tool;
+ struct input_dev *input_dev;
+
+ /* Time stamp the 'TOOL IN' event and add delay. */
+ *stamp = jiffies + PEN_WRITE_DELAY * HZ / 1000;
+
+ switch (pen_end & 0xf0) {
+ /* Stylus Tip in prox */
+ case 0x20:
+ id = STYLUS_DEVICE_ID;
+ tool = BTN_TOOL_PEN;
+ break;
+
+ /* Stylus Eraser in prox */
+ case 0xa0:
+ id = ERASER_DEVICE_ID;
+ tool = BTN_TOOL_RUBBER;
+ break;
+
+ default:
+ id = 0;
+ tool = BTN_TOOL_PEN;
+ break;
+ }
+
+ bosto->current_id = id;
+ bosto->current_tool = tool;
+ input_dev = get_current_input(bosto);
+ input_report_abs(input_dev, ABS_MISC, id);
+ input_report_key(input_dev, tool, 1);
+}
+
+static void bosto_pen_float(struct bosto *bosto, u16 *p, u16 *x, u16 *y,
+ u8 *data)
+{
+ struct input_dev *input_dev;
+ *x = (data[1] << 8) | data[2];      /* Set x ABS */
+ *y = (data[3] << 8) | data[4];      /* Set y ABS */
+ *p = 0;
+
+ input_dev = get_current_input(bosto);
+ input_report_key(input_dev, bosto->current_tool, 1);
+ input_report_key(input_dev, BTN_TOUCH, 0);
+
+ switch (data[0]) {
+ case 0xa0 ... 0xa1:
+ input_report_key(input_dev, BTN_STYLUS, 0);
+ break;
+ case 0xa2 ... 0xa3:
+ input_report_key(input_dev, BTN_STYLUS, 1);
+ break;
+ }
+}
+
+static void bosto_pen_contact(struct bosto *bosto, u16 *p, u16 *x, u16 *y,
+ unsigned long stamp, u8 *data)
+{
+ struct input_dev *input_dev;
+
+ /*
+ * All a little strange; these 4 bytes are always seen whenever the pen
+ * is in contact with the tablet. 'e0 + e1', without the stylus button
+ * pressed and 'e2 + e3' with the stylus button pressed. Either of the
+ * buttons. In either case the byte value jitters between a pair of
+ * either of the two states dependent on the button press.
+ */
+
+ input_dev = get_current_input(bosto);
+ input_report_key(input_dev, bosto->current_tool, 1);
+ input_report_key(input_dev, BTN_TOUCH, 1);
+
+ *x = (data[1] << 8) | data[2];      /* Set x ABS */
+ *y = (data[3] << 8) | data[4];      /* Set y ABS */
+
+ /*
+ * Set 2048 Level pressure sensitivity.
+ * NOTE: The pen button magnifies the pressure sensitivity. Bring
+ * the pen in with the button pressed, ignore the right click response
+ * and keep the button held down. Enjoy the pressure magnification.
+ */
+
+ if (time_after(jiffies, stamp))
+ *p = (data[5] << 3) | ((data[6] & 0xc0) >> 5);
+ else
+ *p = 0;
+
+ switch (data[0]) {
+ case 0xe0 ... 0xe1:
+ input_report_key(input_dev, BTN_STYLUS, 0);  /* no stylus btn */
+ break;
+ case 0xe2 ... 0xe3:
+ input_report_key(input_dev, BTN_STYLUS, 1);  /* stylus btn */
+ break;
+ }
+}
+
+
+static void bosto_parse_packet(struct bosto *bosto)
+{
+ unsigned char *data = bosto->data;
+ struct input_dev *input_dev;
+ struct usb_device *dev = bosto->usbdev;
+ u16 x = 0;
+ u16 y = 0;
+ u16 p = 0;
+ static unsigned long stamp;
+
+ dev_dbg(&dev->dev,
+ "Bosto packet: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ data[0], data[1], data[2], data[3], data[4], data[5], data[6],
+ data[7], data[8], data[9]);
+
+ switch (data[0]) {
+ /* pen event */
+ case 0x02:
+ switch (data[1]) {
+ case 0x80:
+ bosto_tool_out(bosto);
+ dev_dbg(&dev->dev, "TOOL OUT. PEN ID:Tool %x:%x\n",
+ bosto->current_id, bosto->current_tool);
+ break;
+
+ case 0xc2:
+ bosto_tool_in(bosto, &stamp, data[3]);
+ dev_dbg(&dev->dev, "TOOL IN: ID:Tool %x:%x\n",
+ bosto->current_id, bosto->current_tool);
+ break;
+
+ /* Pen trackable but not in contact with screen */
+ case 0xa0 ... 0xa3:
+ bosto_pen_float(bosto, &p, &x, &y, &data[1]);
+ dev_dbg(&dev->dev, "PEN FLOAT: ID:Tool %x:%x\n",
+ bosto->current_id, bosto->current_tool);
+ break;
+
+ /* Pen contact */
+ case 0xe0 ... 0xe3:
+ bosto_pen_contact(bosto, &p, &x, &y, stamp, &data[1]);
+ dev_dbg(&dev->dev, "PEN TOUCH: ID:Tool %x:%x\n",
+ bosto->current_id, bosto->current_tool);
+ break;
+ }
+ break;
+
+ case 0x0c:
+ dev_dbg(&dev->dev,
+ "Tablet Event. Packet data[0]: %02x\n", data[0]);
+ input_dev = get_current_input(bosto);
+ input_report_abs(input_dev, ABS_MISC, bosto->current_id);
+ input_event(input_dev, EV_MSC, MSC_SERIAL, bosto->features->pid);
+ input_sync(input_dev);
+ break;
+
+ default:
+ dev_dbg(&dev->dev,
+ "Error packet. Packet data[0]:  %02x\n", data[0]);
+ break;
+ }
+
+ input_dev = get_current_input(bosto);
+ input_report_abs(input_dev, ABS_X, le16_to_cpup((__le16 *)&x));
+ input_report_abs(input_dev, ABS_Y, le16_to_cpup((__le16 *)&y));
+ input_report_abs(input_dev, ABS_PRESSURE, p);
+ input_event(input_dev, EV_MSC, MSC_SERIAL, bosto->features->pid);
+
+ input_sync(bosto->stylus);
+ input_sync(bosto->eraser);
+}
+
+static void bosto_irq(struct urb *urb)
+{
+ struct bosto *bosto = urb->context;
+ struct usb_device *dev = bosto->usbdev;
+ int retval;
+
+ switch (urb->status) {
+ case 0:
+ /* success */;
+ bosto_parse_packet(bosto);
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dev_err(&dev->dev, "%s - urb shutting down with status: %d",
+ __func__, urb->status);
+ return;
+ default:
+ dev_err(&dev->dev, "%s - nonzero urb status received: %d",
+ __func__, urb->status);
+ break;
+ }
+
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(&dev->dev, "%s - usb_submit_urb failed with result %d",
+ __func__, retval);
+}
+
+static int bosto_stylus_open(struct input_dev *dev)
+{
+ struct bosto *bosto = input_get_drvdata(dev);
+
+ bosto->urb0->dev = bosto->usbdev;
+ if (usb_submit_urb(bosto->urb0, GFP_KERNEL))
+ return -EIO;
+
+ return 0;
+}
+
+static void bosto_stylus_close(struct input_dev *dev)
+{
+ struct bosto *bosto = input_get_drvdata(dev);
+
+ usb_kill_urb(bosto->urb0);
+}
+
+static int bosto_eraser_open(struct input_dev *dev)
+{
+ struct bosto *bosto = input_get_drvdata(dev);
+
+ bosto->urb1->dev = bosto->usbdev;
+ if (usb_submit_urb(bosto->urb1, GFP_KERNEL))
+ return -EIO;
+
+ return 0;
+}
+
+static void bosto_eraser_close(struct input_dev *dev)
+{
+ struct bosto *bosto = input_get_drvdata(dev);
+
+ usb_kill_urb(bosto->urb1);
+}
+
+static bool get_features(struct usb_device *dev, struct bosto *bosto)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(features_array); i++) {
+ if (le16_to_cpu(dev->descriptor.idProduct) == features_array[i].pid) {
+ bosto->features = &features_array[i];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int bosto_create_input_device(struct usb_interface *intf,
+ struct usb_device *dev, struct bosto *bosto, struct urb **urb,
+ struct input_dev **device, const char *device_name,
+ int (*open)(struct input_dev *), void (*close)(struct input_dev *))
+{
+ int i;
+ int status = NO_ERROR;
+ struct usb_endpoint_descriptor *endpoint;
+
+ *urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (*urb) {
+ endpoint = &intf->cur_altsetting->endpoint[0].desc;
+ usb_fill_int_urb(*urb, dev, usb_rcvintpipe(dev,
+ endpoint->bEndpointAddress), bosto->data,
+ bosto->features->pkg_len, bosto_irq, bosto,
+ endpoint->bInterval);
+
+ (*urb)->transfer_dma = bosto->data_dma;
+ (*urb)->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ *device = input_allocate_device();
+ if (*device) {
+ usb_make_path(dev, bosto->phys, sizeof(bosto->phys));
+ strlcat(bosto->phys, "/input0", sizeof(bosto->phys));
+ (*device)->phys = bosto->phys;
+
+ (*device)->name = device_name;
+ usb_to_input_id(dev, &(*device)->id);
+
+ (*device)->dev.parent = &intf->dev;
+
+ input_set_drvdata(*device, bosto);
+
+ (*device)->open  = open;
+ (*device)->close = close;
+
+ for (i = 0; i < ARRAY_SIZE(hw_eventtypes); ++i)
+ __set_bit(hw_eventtypes[i], (*device)->evbit);
+
+ for (i = 0; i < ARRAY_SIZE(hw_absevents); ++i)
+ __set_bit(hw_absevents[i], (*device)->absbit);
+
+ for (i = 0; i < ARRAY_SIZE(hw_btnevents); ++i)
+ __set_bit(hw_btnevents[i], (*device)->keybit);
+
+ for (i = 0; i < ARRAY_SIZE(hw_mscevents); ++i)
+ __set_bit(hw_mscevents[i], (*device)->mscbit);
+
+ input_set_abs_params(*device, ABS_X, 0,
+ bosto->features->max_x, 0, 0);
+ input_set_abs_params(*device, ABS_Y, 0,
+ bosto->features->max_y, 0, 0);
+ input_set_abs_params(*device, ABS_PRESSURE, 0,
+ bosto->features->max_pressure, 0, 0);
+
+ status = input_register_device(*device);
+ if (status != NO_ERROR) {
+ input_free_device(*device);
+ usb_free_urb(*urb);
+ }
+ } else {
+ usb_free_urb(*urb);
+ status = -ENOMEM;
+ }
+ } else {
+ status = -ENOMEM;
+ }
+ return status;
+}
+
+static int bosto_probe(struct usb_interface *intf, const struct
usb_device_id *id)
+{
+ struct bosto *bosto;
+ int status = NO_ERROR;
+ struct usb_device *dev = interface_to_usbdev(intf);
+
+ dev_info(&dev->dev, "Bosto_Probe checking Tablet\n");
+
+ bosto = kzalloc(sizeof(struct bosto), GFP_KERNEL);
+
+ if (bosto && get_features(dev, bosto)) {
+ bosto->data = usb_alloc_coherent(dev, bosto->features->pkg_len,
+ GFP_KERNEL, &bosto->data_dma);
+
+ if (bosto->data != NULL) {
+ bosto->usbdev = dev;
+ strlcpy(bosto->stylus_name, bosto->features->name,
+ sizeof(bosto->stylus_name));
+ strlcat(bosto->stylus_name, " stylus",
+ sizeof(bosto->stylus_name));
+ strlcpy(bosto->eraser_name, bosto->features->name,
+ sizeof(bosto->eraser_name));
+ strlcat(bosto->eraser_name, " eraser",
+ sizeof(bosto->eraser_name));
+
+ status = bosto_create_input_device(intf, dev, bosto,
+ &bosto->urb0, &bosto->stylus,
+ bosto->stylus_name, bosto_stylus_open,
+ bosto_stylus_close);
+
+ if (status == NO_ERROR) {
+ status = bosto_create_input_device(intf, dev,
+ bosto, &bosto->urb1, &bosto->eraser,
+ bosto->eraser_name, bosto_eraser_open,
+ bosto_eraser_close);
+ }
+
+ if (status == NO_ERROR) {
+ usb_set_intfdata(intf, bosto);
+ return status;
+ }
+
+ usb_free_coherent(dev, bosto->features->pkg_len,
+ bosto->data, bosto->data_dma);
+ }
+
+ } else {
+ status = -ENOMEM;
+ }
+
+ kfree(bosto);
+ return status;
+}
+
+static void bosto_disconnect(struct usb_interface *intf)
+{
+ struct bosto *bosto = usb_get_intfdata(intf);
+
+ input_unregister_device(bosto->stylus);
+ input_unregister_device(bosto->eraser);
+ usb_free_urb(bosto->urb1);
+ usb_free_urb(bosto->urb0);
+ usb_free_coherent(interface_to_usbdev(intf), bosto->features->pkg_len,
+ bosto->data, bosto->data_dma);
+ kfree(bosto);
+ usb_set_intfdata(intf, NULL);
+}
+
+static const struct usb_device_id bosto_ids[] = {
+ { USB_DEVICE_AND_INTERFACE_INFO(
+ USB_VENDOR_ID_HANWANG,
+ USB_DEVICE_ID_BOSTO14WA_2,
+ BOSTO_TABLET_INT_CLASS,
+ BOSTO_TABLET_INT_SUB_CLASS,
+ BOSTO_TABLET_INT_PROTOCOL) },
+ {}
+};
+
+
+MODULE_DEVICE_TABLE(usb, bosto_ids);
+
+static struct usb_driver bosto_driver = {
+ .name       = "bosto_14wa",
+ .probe      = bosto_probe,
+ .disconnect = bosto_disconnect,
+ .id_table   = bosto_ids,
+};
+
+static int __init bosto_init(void)
+{
+ printk(KERN_INFO "Bosto 2nd Generation USB Driver module being
initialised\n");
+ return usb_register(&bosto_driver);
+}
+
+static void __exit bosto_exit(void)
+{
+ usb_deregister(&bosto_driver);