New input driver for eBeam device (ask for review)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

I'm working on a input driver for usb eBeam devices. It's time for a first
review, so i brute-post the code below.
Tell me if there's a better way to do it, i'm not familiar with the list. BTW,
is there a complete "howto contrib driver for noob" somewhere ?

Some infos :
- It's a pointing device.

- http://www.e-beam.com/products/overview.html
  I work with a "classic" model.

- Currently, these devices are only supported on some specific distro via 
  user-space, closed source, daemon and tools, including an xorg input driver.

- The device reports positions in it's own cartesian coordinate system, i use
  an homography to compute screen ones (strange formulas in ebeam_calculate_xy).

- code tested against a 3.3.6 kernel (mageia 2).

Thanks in advance.

###############

/******************************************************************************
 *
 * eBeam driver
 *
 * Copyright (C) 2012 Yann Cantin (yann.cantin@xxxxxxxxxxx)
 *
 *      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.
 *
 *  based on
 *
 *    usbtouchscreen.c by Daniel Ritz <daniel.ritz@xxxxxx>
 *    aiptek.c (sysfs/settings) by Chris Atenasio <chris@xxxxxxxx>
 *                                 Bryan W. Headley <bwheadley@xxxxxxxxxxxxx>
 *
 *****************************************************************************/

#define DEBUG

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <linux/usb/input.h>
#include <linux/hid.h>

#define DRIVER_VERSION          "v0.3"
#define DRIVER_AUTHOR           "Yann Cantin <yann.cantin@xxxxxxxxxxx>"
#define DRIVER_DESC             "USB eBeam Driver"

#define USB_VENDOR_ID_EFI       0x2650     /* Electronics For Imaging, Inc   */
#define USB_PRODUCT_ID_CLASSIC  0x1311     /* Classic projection "La banane" */

#define EBEAM_BTN_TIP           0x1      /* tip    */
#define EBEAM_BTN_LIT           0x2      /* little */
#define EBEAM_BTN_BIG           0x4      /* big    */

/* until KConfig */
#define CONFIG_EBEAM_USB_CLASSIC

/* device specifc data/functions */
struct ebeam_device;
struct ebeam_device_info {
        int min_X;
        int max_X;
        int min_Y;
        int max_Y;

        /*
         * TODO : Check if it's really necessary, waiting for other device info.
         * Always service the USB devices irq not just when the input device is
         * open. This is useful when devices have a watchdog which prevents us
         * from periodically polling the device. Leave this unset unless your
         * ebeam device requires it, as it does consume more of the USB
         * bandwidth.
         */
        bool irq_always;

        int rept_size;

        /* optional, generic exist */
        void (*process_pkt)  (struct ebeam_device *ebeam,
                              unsigned char *pkt,
                              int len);

        /* mandatory, model-specific */
        int  (*read_data)    (struct ebeam_device *ebeam,
                              unsigned char *pkt);
        void (*setup_input)  (struct ebeam_device *ebeam,
                              struct input_dev *input_dev);
        void (*report_input) (struct ebeam_device *ebeam);

        /* optional, model-specific */
        int  (*alloc)        (struct ebeam_device *ebeam);
        int  (*init)         (struct ebeam_device *ebeam);
        void (*exit)         (struct ebeam_device *ebeam);
};

/* ebeam settings */
struct ebeam_settings {
        int min_x;
        int max_x;
        int min_y;
        int max_y;

        /* H matrix */
        s64 h1;
        s64 h2;
        s64 h3;
        s64 h4;
        s64 h5;
        s64 h6;
        s64 h7;
        s64 h8;
        s64 h9;
};

/* ebeam device */
struct ebeam_device {
        unsigned char            *data;
        dma_addr_t               data_dma;
        unsigned char            *buffer;
        int                      buf_len;
        struct urb               *irq;
        struct usb_interface     *interface;
        struct input_dev         *input;
        struct ebeam_device_info *type;
        char                     name[128];
        char                     phys[64];
        void                     *priv;

        struct ebeam_settings    cursetting;  /* device's current settings */
        struct ebeam_settings    newsetting;  /* ... and new ones          */

        bool                     calibrated;  /* false : send raw
                                               * true  : send computed     */
        u16                      X, Y;        /* raw coordinates           */
        int                      x, y;        /* computed coordinates      */
        int                      btn_map;     /* internal buttons map      */
};


/* device types */
enum {
        DEVTYPE_IGNORE = -1,
        DEVTYPE_CLASSIC,
};

static const struct usb_device_id ebeam_devices[] = {
#ifdef CONFIG_EBEAM_USB_CLASSIC
        {USB_DEVICE(USB_VENDOR_ID_EFI, USB_PRODUCT_ID_CLASSIC),
                .driver_info = DEVTYPE_CLASSIC},
#endif
        {}
};

/*******************************************************************************
 * sysfs part
 */

/*
 * screen min/max and H matrix coefs
 * _get return *current* value
 * _set set new value
 */
#define DEVICE_MINMAX_ATTR(MM)                                                 \
static ssize_t ebeam_##MM##_get(struct device *dev,                            \
                                struct device_attribute *attr,                 \
                                char *buf)                                     \
{                                                                              \
        struct ebeam_device *ebeam = dev_get_drvdata(dev);                     \
                                                                               \
        return snprintf(buf, PAGE_SIZE, "%d\n", ebeam->cursetting.MM);         \
}                                                                              \
static ssize_t ebeam_##MM##_set(struct device *dev,                            \
                                struct device_attribute *attr,                 \
                                const char *buf,                               \
                                size_t count)                                  \
{                                                                              \
        struct ebeam_device *ebeam = dev_get_drvdata(dev);                     \
        int err, MM;                                                           \
                                                                               \
        err = kstrtoint(buf, 10, &MM);                                         \
        if (err)                                                               \
                return err;                                                    \
                                                                               \
        ebeam->newsetting.MM = MM;                                             \
        return count;                                                          \
}                                                                              \
static DEVICE_ATTR(MM, S_IRUGO | S_IWUGO,                                      \
                   ebeam_##MM##_get,                                           \
                   ebeam_##MM##_set)

DEVICE_MINMAX_ATTR(min_x);
DEVICE_MINMAX_ATTR(max_x);
DEVICE_MINMAX_ATTR(min_y);
DEVICE_MINMAX_ATTR(max_y);

#define DEVICE_H_ATTR(SET_ID)                                                  \
static ssize_t ebeam_h##SET_ID##_get(struct device *dev,                       \
                                     struct device_attribute *attr,            \
                                     char *buf)                                \
{                                                                              \
        struct ebeam_device *ebeam = dev_get_drvdata(dev);                     \
                                                                               \
        return snprintf(buf, PAGE_SIZE, "%lld\n", ebeam->cursetting.h##SET_ID);\
}                                                                              \
static ssize_t ebeam_h##SET_ID##_set(struct device *dev,                       \
                                     struct device_attribute *attr,            \
                                     const char *buf,                          \
                                     size_t count)                             \
{                                                                              \
        struct ebeam_device *ebeam = dev_get_drvdata(dev);                     \
        int err;                                                               \
        u64 h;                                                                 \
                                                                               \
        err = kstrtoll(buf, 10, &h);                                           \
        if (err)                                                               \
                return err;                                                    \
                                                                               \
        ebeam->newsetting.h##SET_ID = h;                                       \
        return count;                                                          \
}                                                                              \
static DEVICE_ATTR(h##SET_ID, S_IRUGO | S_IWUGO,                               \
                   ebeam_h##SET_ID##_get,                                      \
                   ebeam_h##SET_ID##_set)

DEVICE_H_ATTR(1);
DEVICE_H_ATTR(2);
DEVICE_H_ATTR(3);
DEVICE_H_ATTR(4);
DEVICE_H_ATTR(5);
DEVICE_H_ATTR(6);
DEVICE_H_ATTR(7);
DEVICE_H_ATTR(8);
DEVICE_H_ATTR(9);

/*
 * sysfs calibrated
 * Once H matrix coefs are set, writing 1 to this file triggers
 * coordinates mapping.
 * Anything else reset the device to un-calibrated mode,
 * storing cursetting in newsetting.
*/
static ssize_t ebeam_calibrated_get(struct device *dev,
                                    struct device_attribute *attr,
                                    char *buf)
{
        struct ebeam_device *ebeam = dev_get_drvdata(dev);

        return snprintf(buf, PAGE_SIZE, "%d\n", ebeam->calibrated);
}

static ssize_t ebeam_calibrated_set(struct device *dev,
                                    struct device_attribute *attr,
                                    const char *buf,
                                    size_t count)
{
        struct ebeam_device *ebeam = dev_get_drvdata(dev);
        int err, c;

        /* TODO : check if we need to suspend irq */

        err = kstrtoint(buf, 10, &c);
        if (err)
                return err;

        if (c == 1) {
                memcpy(&ebeam->cursetting, &ebeam->newsetting,
                       sizeof(struct ebeam_settings));
                ebeam->calibrated = true;
                ebeam->type->setup_input(ebeam, ebeam->input);
        } else {
                memcpy(&ebeam->newsetting, &ebeam->cursetting,
                       sizeof(struct ebeam_settings));
                ebeam->calibrated = false;
                ebeam->type->setup_input(ebeam, ebeam->input);
        }

        return count;
}

static DEVICE_ATTR(calibrated, S_IRUGO | S_IWUGO,
                   ebeam_calibrated_get, ebeam_calibrated_set);

static struct attribute *ebeam_attrs[] = {
        &dev_attr_min_x.attr,
        &dev_attr_min_y.attr,
        &dev_attr_max_x.attr,
        &dev_attr_max_y.attr,
        &dev_attr_h1.attr,
        &dev_attr_h2.attr,
        &dev_attr_h3.attr,
        &dev_attr_h4.attr,
        &dev_attr_h5.attr,
        &dev_attr_h6.attr,
        &dev_attr_h7.attr,
        &dev_attr_h8.attr,
        &dev_attr_h9.attr,
        &dev_attr_calibrated.attr,
        NULL
};

static const struct attribute_group ebeam_attr_group = {
        .attrs = ebeam_attrs,
};

/*******************************************************************************
 * classic projection Part
 */

#ifdef CONFIG_EBEAM_USB_CLASSIC
/* IRQ */
static int classic_read_data(struct ebeam_device *ebeam, unsigned char *pkt)
{

/*
 *    Packet description :
 *
 *    nop packet : FF FF FF FF FF FF FF FF
 *
 *    pkt[0] : Sensors
 *             bit 1 : ultrasound signal (guessed)
 *             bit 2 : IR signal (tested with a remote...) ;
 *             readings OK : 0x03 (anything else is a show-stopper)
 *
 *    pkt[1] : X low
 *    pkt[2] : X high
 *
 *    pkt[3] : Y low
 *    pkt[4] : Y high
 *
 *    pkt[5] : fiability ?
 *             often 0xC0
 *             > 0x80 : OK
 *
 *    pkt[6] : buttons state
 *             0x1 = no buttons
 *             bit 1 : tip (WARNING inversed : 0=pressed)
 *             bit 2 : ? (always 0 during tests)
 *             bit 3 : little (1=pressed)
 *             bit 4 : big (1=pressed)
 *
 *             pointer ID : Tested  : 0x6=wand ;
 *                          Guessed : 0x1=red ; 0x2=blue ; 0x3=green ;
 *                                    0x4=black ; 0x5=eraser
 *             bit 5 : pointer ID
 *             bit 6 : pointer ID
 *             bit 7 : pointer ID
 *             bit 8 : pointer ID
 *
 *
 *    pkt[7] : fiability ?
 *             often 0xFF
 *
 */

        /* Filtering bad/nop packet */
        if ( pkt[0] != 0x03 )
          return 0;

        ebeam->X = (pkt[2] << 8) | pkt[1];
        ebeam->Y = (pkt[4] << 8) | pkt[3];

        ebeam->btn_map = (!(pkt[6] & 0x1)) |
                         ((pkt[6] & 0x4) >> 1) |
                         ((pkt[6] & 0x8) >> 1);

        return 1;
}

/* IRQ */
static void classic_report_input(struct ebeam_device *ebeam)
{
        input_report_key(ebeam->input, BTN_LEFT,
                         (ebeam->btn_map & EBEAM_BTN_TIP));
        input_report_key(ebeam->input, BTN_MIDDLE,
                         (ebeam->btn_map & EBEAM_BTN_LIT));
        input_report_key(ebeam->input, BTN_RIGHT,
                         (ebeam->btn_map & EBEAM_BTN_BIG));

        input_report_abs(ebeam->input, ABS_X, ebeam->x);
        input_report_abs(ebeam->input, ABS_Y, ebeam->y);

        input_sync(ebeam->input);
}

static void classic_setup_input(struct ebeam_device *ebeam,
                                struct input_dev *input_dev)
{
        /* Properties */
        set_bit(INPUT_PROP_DIRECT,  input_dev->propbit);

        /* Events generated */
        set_bit(EV_KEY, input_dev->evbit);
        set_bit(EV_ABS, input_dev->evbit);

        /* Keys */
        set_bit(BTN_LEFT,   input_dev->keybit);
        set_bit(BTN_MIDDLE, input_dev->keybit);
        set_bit(BTN_RIGHT,  input_dev->keybit);

        /* Axis */
        if (!ebeam->calibrated) {
                ebeam->cursetting.min_x = ebeam->type->min_X;
                ebeam->cursetting.max_x = ebeam->type->max_X;
                ebeam->cursetting.min_y = ebeam->type->min_Y;
                ebeam->cursetting.max_y = ebeam->type->max_Y;
        }

        input_set_abs_params(input_dev, ABS_X,
                             ebeam->cursetting.min_x, ebeam->cursetting.max_x,
                             0, 0);
        input_set_abs_params(input_dev, ABS_Y,
                             ebeam->cursetting.min_y, ebeam->cursetting.max_y,
                             0, 0);
}

#endif

/*****************************************************************************
 * device descriptors
 */
static struct ebeam_device_info ebeam_dev_info[] = {
#ifdef CONFIG_EBEAM_USB_CLASSIC
        [DEVTYPE_CLASSIC] = {
                .min_X          = 0,
                .max_X          = 65535,
                .min_Y          = 0,
                .max_Y          = 65535,
                .rept_size      = 8,
                .read_data      = classic_read_data,
                .setup_input    = classic_setup_input,
                .report_input   = classic_report_input,
        },
#endif
};

/*******************************************************************************
 * Generic Part
 * Nothing model-specific below this point
 */

static void ebeam_init_settings(struct ebeam_device *ebeam)
{
        ebeam->calibrated = false;

        /* Init (x,y) min/max to raw ones */
        ebeam->cursetting.min_x = ebeam->newsetting.min_x = ebeam->type->min_X;
        ebeam->cursetting.max_x = ebeam->newsetting.max_x = ebeam->type->max_X;
        ebeam->cursetting.min_y = ebeam->newsetting.min_y = ebeam->type->min_Y;
        ebeam->cursetting.max_y = ebeam->newsetting.max_y = ebeam->type->max_Y;

        /* Safe values for the H matrix (Identity) */
        ebeam->cursetting.h1 = ebeam->newsetting.h1 = 1;
        ebeam->cursetting.h2 = ebeam->newsetting.h2 = 0;
        ebeam->cursetting.h3 = ebeam->newsetting.h3 = 0;

        ebeam->cursetting.h4 = ebeam->newsetting.h4 = 0;
        ebeam->cursetting.h5 = ebeam->newsetting.h5 = 1;
        ebeam->cursetting.h6 = ebeam->newsetting.h6 = 0;

        ebeam->cursetting.h7 = ebeam->newsetting.h7 = 0;
        ebeam->cursetting.h8 = ebeam->newsetting.h8 = 0;
        ebeam->cursetting.h9 = ebeam->newsetting.h9 = 1;
}

/*
 * IRQ
 * compute screen coordinates from raw
 * Overflow and negative values are user space's problem
 */
static bool ebeam_calculate_xy(struct ebeam_device *ebeam)
{
        /* TODO : check if u64 division is available on all plateform */

        s64 scale;

        if (!ebeam->calibrated) {
                ebeam->x = ebeam->X;
                ebeam->y = ebeam->Y;
        } else {
                scale = ebeam->cursetting.h7 * ebeam->X +
                        ebeam->cursetting.h8 * ebeam->Y +
                        ebeam->cursetting.h9;

                /* Who want a division by zero in kernel ? */
                if (scale == 0) {
                        dev_err(&(ebeam->interface)->dev,
                                "%s - Division by zero, wrong calibration.\n",
                                __func__);
                        dev_err(&(ebeam->interface)->dev,
                                "%s - Resetting to un-calibrated mode.\n",
                                __func__);
                        ebeam->calibrated = false;
                        return 0;
                }

                /*
                 * We *must* round the result, but not with (int) (v1/v2 + 0.5)
                 *
                 * (int) (v1/v2 + 0.5) <=> (int) ( (2*v1 + v2)/(2*v2) )
                 */
                ebeam->x = (int) ((((ebeam->cursetting.h1 * ebeam->X +
                                     ebeam->cursetting.h2 * ebeam->Y +
                                     ebeam->cursetting.h3) << 1) + scale) /
                                     (scale << 1));
                ebeam->y = (int) ((((ebeam->cursetting.h4 * ebeam->X +
                                     ebeam->cursetting.h5 * ebeam->Y +
                                     ebeam->cursetting.h6) << 1) + scale) /
                                     (scale << 1));
        }

        return 1;
}

/* IRQ */
/* generic function, may be overloaded */
static void ebeam_process_pkt(struct ebeam_device *ebeam,
                              unsigned char *pkt, int len)
{
        struct ebeam_device_info *type = ebeam->type;

        if (!type->read_data(ebeam, pkt))
                return;

        if (!ebeam_calculate_xy(ebeam))
                return;

        type->report_input(ebeam);
}

/* IRQ
 * handler */
static void ebeam_irq(struct urb *urb)
{
        struct ebeam_device *ebeam = urb->context;
        struct device *dev = &ebeam->interface->dev;
        int retval;

        switch (urb->status) {
        case 0:
                /* success */
                break;
        case -ETIME:
                /* this urb is timing out */
                dev_dbg(dev,
                        "%s - urb timed out - was the device unplugged?\n",
                        __func__);
                return;
        case -ECONNRESET:
        case -ENOENT:
        case -ESHUTDOWN:
        case -EPIPE:
                /* this urb is terminated, clean up */
                dev_dbg(dev, "%s - urb shutting down with status: %d\n",
                        __func__, urb->status);
                return;
        default:
                dev_dbg(dev, "%s - nonzero urb status received: %d\n",
                        __func__, urb->status);
                goto exit;
        }

        ebeam->type->process_pkt(ebeam, ebeam->data, urb->actual_length);

exit:
        usb_mark_last_busy(interface_to_usbdev(ebeam->interface));
        retval = usb_submit_urb(urb, GFP_ATOMIC);
        if (retval)
                dev_err(dev, "%s - usb_submit_urb failed with result: %d\n",
                        __func__, retval);
}

static int ebeam_open(struct input_dev *input)
{
        struct ebeam_device *ebeam = input_get_drvdata(input);
        int r;

        ebeam->irq->dev = interface_to_usbdev(ebeam->interface);

        r = usb_autopm_get_interface(ebeam->interface) ? -EIO : 0;
        if (r < 0)
                goto out;

        if (!ebeam->type->irq_always) {
                if (usb_submit_urb(ebeam->irq, GFP_KERNEL)) {
                        r = -EIO;
                        goto out_put;
                }
        }

        ebeam->interface->needs_remote_wakeup = 1;
out_put:
        usb_autopm_put_interface(ebeam->interface);
out:
        return r;
}

static void ebeam_close(struct input_dev *input)
{
        struct ebeam_device *ebeam = input_get_drvdata(input);
        int r;

        if (!ebeam->type->irq_always)
                usb_kill_urb(ebeam->irq);

        r = usb_autopm_get_interface(ebeam->interface);
        ebeam->interface->needs_remote_wakeup = 0;

        if (!r)
                usb_autopm_put_interface(ebeam->interface);
}

static int ebeam_suspend(struct usb_interface *intf, pm_message_t message)
{
        struct ebeam_device *ebeam = usb_get_intfdata(intf);

        usb_kill_urb(ebeam->irq);

        return 0;
}

static int ebeam_resume(struct usb_interface *intf)
{
        struct ebeam_device *ebeam = usb_get_intfdata(intf);
        struct input_dev *input = ebeam->input;
        int result = 0;

        mutex_lock(&input->mutex);
        if (input->users || ebeam->type->irq_always)
                result = usb_submit_urb(ebeam->irq, GFP_NOIO);
        mutex_unlock(&input->mutex);

        return result;
}

static int ebeam_reset_resume(struct usb_interface *intf)
{
        struct ebeam_device *ebeam = usb_get_intfdata(intf);
        struct input_dev *input = ebeam->input;
        int err = 0;

        /* reinit the device */
        if (ebeam->type->init) {
                err = ebeam->type->init(ebeam);
                if (err) {
                        dev_dbg(&intf->dev,
                                "%s - type->init() failed, err: %d\n",
                                __func__, err);
                        return err;
                }
        }

        /* restart IO if needed */
        mutex_lock(&input->mutex);
        if (input->users)
                err = usb_submit_urb(ebeam->irq, GFP_NOIO);
        mutex_unlock(&input->mutex);

        return err;
}

static void ebeam_free_buffers(struct usb_device *udev,
                               struct ebeam_device *ebeam)
{
        usb_free_coherent(udev, ebeam->type->rept_size,
                          ebeam->data, ebeam->data_dma);
        kfree(ebeam->buffer);
}

static struct usb_endpoint_descriptor *
ebeam_get_input_endpoint(struct usb_host_interface *interface)
{
        int i;

        for (i = 0; i < interface->desc.bNumEndpoints; i++)
                if (usb_endpoint_dir_in(&interface->endpoint[i].desc))
                        return &interface->endpoint[i].desc;

        return NULL;
}

static int ebeam_probe(struct usb_interface *intf,
                       const struct usb_device_id *id)
{
        struct ebeam_device *ebeam;
        struct input_dev *input_dev;
        struct usb_endpoint_descriptor *endpoint;
        struct usb_device *udev = interface_to_usbdev(intf);
        struct ebeam_device_info *type;
        int err = -ENOMEM;

        /* some devices are ignored */
        if (id->driver_info == DEVTYPE_IGNORE)
                return -ENODEV;

        endpoint = ebeam_get_input_endpoint(intf->cur_altsetting);
        if (!endpoint)
                return -ENXIO;

        ebeam = kzalloc(sizeof(struct ebeam_device), GFP_KERNEL);
        input_dev = input_allocate_device();
        if (!ebeam || !input_dev)
                goto out_free;

        type = &ebeam_dev_info[id->driver_info];
        ebeam->type = type;
        ebeam_init_settings(ebeam);

        if (!type->process_pkt)
                type->process_pkt = ebeam_process_pkt;

        ebeam->data = usb_alloc_coherent(udev, type->rept_size,
                                         GFP_KERNEL, &ebeam->data_dma);
        if (!ebeam->data)
                goto out_free;

        ebeam->irq = usb_alloc_urb(0, GFP_KERNEL);
        if (!ebeam->irq) {
                dev_dbg(&intf->dev,
                        "%s - usb_alloc_urb failed: ebeam->irq\n", __func__);
                goto out_free_buffers;
        }

        ebeam->interface = intf;
        ebeam->input = input_dev;

        /* setup name */
        snprintf(ebeam->name, sizeof(ebeam->name),
                 "USB eBeam %04x:%04x",
                 le16_to_cpu(udev->descriptor.idVendor),
                 le16_to_cpu(udev->descriptor.idProduct));

        if (udev->manufacturer || udev->product) {
                strlcat(ebeam->name,
                        " (",
                        sizeof(ebeam->name));

                if (udev->manufacturer)
                        strlcat(ebeam->name,
                                udev->manufacturer,
                                sizeof(ebeam->name));

                if (udev->product) {
                        if (udev->manufacturer)
                                strlcat(ebeam->name,
                                        " ",
                                        sizeof(ebeam->name));
                        strlcat(ebeam->name,
                                udev->product,
                                sizeof(ebeam->name));
                }

                strlcat(ebeam->name, ")", sizeof(ebeam->name));
        }

        /* usb tree */
        usb_make_path(udev, ebeam->phys, sizeof(ebeam->phys));
        strlcat(ebeam->phys, "/input0", sizeof(ebeam->phys));

        /* input setup */
        input_dev->name = ebeam->name;
        input_dev->phys = ebeam->phys;
        usb_to_input_id(udev, &input_dev->id);
        input_dev->dev.parent = &intf->dev;

        input_set_drvdata(input_dev, ebeam);

        input_dev->open = ebeam_open;
        input_dev->close = ebeam_close;

        /* usb urb setup */
        if (usb_endpoint_type(endpoint) == USB_ENDPOINT_XFER_INT)
                usb_fill_int_urb(ebeam->irq, udev,
                         usb_rcvintpipe(udev, endpoint->bEndpointAddress),
                         ebeam->data, type->rept_size,
                         ebeam_irq, ebeam, endpoint->bInterval);
        else
                usb_fill_bulk_urb(ebeam->irq, udev,
                         usb_rcvbulkpipe(udev, endpoint->bEndpointAddress),
                         ebeam->data, type->rept_size,
                         ebeam_irq, ebeam);

        ebeam->irq->dev = udev;
        ebeam->irq->transfer_dma = ebeam->data_dma;
        ebeam->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

        /* device specific allocations */
        if (type->alloc) {
                err = type->alloc(ebeam);
                if (err) {
                        dev_dbg(&intf->dev,
                                "%s - type->alloc() failed, err: %d\n",
                                __func__, err);
                        goto out_free_urb;
                }
        }

        /* device specific initialisation */
        if (type->init) {
                err = type->init(ebeam);
                if (err) {
                        dev_dbg(&intf->dev,
                                "%s - type->init() failed, err: %d\n",
                                __func__, err);
                        goto out_do_exit;
                }
        }

        /* input final setup */
        err = input_register_device(ebeam->input);
        if (err) {
                dev_dbg(&intf->dev,
                        "%s - input_register_device failed, err: %d\n",
                        __func__, err);
                goto out_do_exit;
        }

        type->setup_input(ebeam, input_dev);

        /* usb final setup */
        usb_set_intfdata(intf, ebeam);

        if (ebeam->type->irq_always) {
                /* this can't fail */
                usb_autopm_get_interface(intf);
                err = usb_submit_urb(ebeam->irq, GFP_KERNEL);
                if (err) {
                        usb_autopm_put_interface(intf);
                        dev_err(&intf->dev,
                                "%s - usb_submit_urb failed with result: %d\n",
                                __func__, err);
                        goto out_unregister_input;
                }
        }

        /* sysfs setup */
        err = sysfs_create_group(&intf->dev.kobj, &ebeam_attr_group);
        if (err) {
                dev_dbg(&intf->dev,
                        "%s - cannot create sysfs group, err: %d\n",
                        __func__, err);
                goto out_unregister_input;
        }

        return 0;

out_unregister_input:
        input_unregister_device(input_dev);
        input_dev = NULL;
out_do_exit:
        if (type->exit)
                type->exit(ebeam);
out_free_urb:
        usb_free_urb(ebeam->irq);
out_free_buffers:
        ebeam_free_buffers(udev, ebeam);
out_free:
        input_free_device(input_dev);
        kfree(ebeam);
        return err;
}

static void ebeam_disconnect(struct usb_interface *intf)
{
        struct ebeam_device *ebeam = usb_get_intfdata(intf);

        if (!ebeam)
                return;

        dev_dbg(&intf->dev,
                "%s - ebeam is initialized, cleaning up\n", __func__);

        usb_set_intfdata(intf, NULL);
        /* this will stop IO via close */
        input_unregister_device(ebeam->input);
        sysfs_remove_group(&intf->dev.kobj, &ebeam_attr_group);
        usb_free_urb(ebeam->irq);
        if (ebeam->type->exit)
                ebeam->type->exit(ebeam);
        ebeam_free_buffers(interface_to_usbdev(intf), ebeam);
        kfree(ebeam);
}

MODULE_DEVICE_TABLE(usb, ebeam_devices);

static struct usb_driver ebeam_driver = {
        .name           = "ebeam",
        .probe          = ebeam_probe,
        .disconnect     = ebeam_disconnect,
        .suspend        = ebeam_suspend,
        .resume         = ebeam_resume,
        .reset_resume   = ebeam_reset_resume,
        .id_table       = ebeam_devices,
        .supports_autosuspend = 1,
};

module_usb_driver(ebeam_driver);

MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

###############

-- 
Yann Cantin
A4FEB47F
--
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux