Hi,

This is a attempt to replace the closed source ebeam xinput driver provided by 
Luidia for ebeam device.
(see http://www.e-beam.com/business/ebeam-classic-projection/overview.html).

I have to do that cause their driver don't support recent xorg version. Support 
says they are working
on a re-implementation from scratch, but my device is unusable since i update 
my distro 7 mouths back, so...

One particularity of ebeam devices handling is that it use a 2 parts system : a 
daemon service that talk directly
to the hardware (usb for mine, there is also bluetouth or serial ones), and a 
xinput driver. The 2 communicate
thru a network socket via binary packets. The good news is that the daemon run 
perfectly and seems to provide a
common interface for multiple devices, the bad one is that it is also closed, 
so this driver can't work alone,
and need a proprietary part to be useful. I can live with that, but i don't 
know how you xorg guys can deal with.

Anyway, the driver works. I'm sure i've reproduce all the original driver's 
mechanics (it's vrey basic), and i
try hard to stick to coding rules i've found in other xinput drivers, but as 
you may know, there's no clear
documentation out there to help beginner like me.

There is clearly space for optimization in the code, but i mainly worry about 
correctness for now.

Many thanks for the time you'll spend.

-- 
Yann Cantin
A4FEB47F
--
/*
 * Copyright 2010 Yann Cantin <[email protected]>
 *
 * Parts inspired from Peter Hutterer and Przemysław Firszt "random"
 * driver, logic reverse-engeniered from the old (closed) Luida driver.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without
 * fee, provided that the above copyright notice appear in all copies
 * and that both that copyright notice and this permission notice
 * appear in supporting documentation, and that the name of Red Hat
 * not be used in advertising or publicity pertaining to distribution
 * of the software without specific, written prior permission.  Red
 * Hat makes no representations about the suitability of this software
 * for any purpose.  It is provided "as is" without express or implied
 * warranty.
 *
 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
 * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <linux/input.h>
#include <linux/types.h>

#include <xf86_OSproc.h>

#include <unistd.h>

#include <xf86.h>
#include <xf86Xinput.h>
#include <exevents.h>
#include <xorgVersion.h>
#include <xkbsrv.h>

#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3
#define HAVE_PROPERTIES 1
#endif

#ifdef HAVE_PROPERTIES
#include <xserver-properties.h>
/* 1.6 has properties, but no labels */
#ifdef AXIS_LABEL_PROP
#define HAVE_LABELS
#else
#undef HAVE_LABELS
#endif
#endif

#include <stdio.h>
#include <sys/stat.h>
#include <sys/syscall.h>

/* network socket */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
#include <xorg-server.h>
#include <xorgVersion.h>
#include <xf86Module.h>
#include <X11/Xatom.h>

#include "ebeam.h"

static InputInfoPtr EbeamInit(InputDriverPtr drv, IDevPtr dev, int flags);
static void         EbeamUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags);
static pointer      EbeamPlug(pointer module, pointer options, int *errmaj, int *errmin);
static void         EbeamUnplug(pointer p);
static void         EbeamReadInput(InputInfoPtr pInfo);
static int          EbeamControl(DeviceIntPtr device,int what);

static int          _ebeam_open_socket(InputInfoPtr pInfo);
static int          _ebeam_init_buttons(DeviceIntPtr device);
static int          _ebeam_init_axes(DeviceIntPtr device);

/* packet buffer */
char packetbuf[256 * MAX_PACKET];


_X_EXPORT InputDriverRec EBEAM =
{
    1,
    "ebeam",
    NULL,
    EbeamInit,
    EbeamUnInit,
    NULL,
    0
};

static XF86ModuleVersionInfo EbeamVersionRec =
{
    "ebeam",
    MODULEVENDORSTRING,
    MODINFOSTRING1,
    MODINFOSTRING2,
    XORG_VERSION_CURRENT,
    PACKAGE_VERSION_MAJOR, PACKAGE_VERSION_MINOR,
    PACKAGE_VERSION_PATCHLEVEL,
    ABI_CLASS_XINPUT,
    ABI_XINPUT_VERSION,
    MOD_CLASS_XINPUT,
    {0, 0, 0, 0}
};

_X_EXPORT XF86ModuleData ebeamModuleData =
{
    &EbeamVersionRec,
    &EbeamPlug,
    &EbeamUnplug
};

static void EbeamUnplug(pointer p)
{
};

static pointer EbeamPlug(pointer module,
                         pointer options,
                         int     *errmaj,
                         int     *errmin)
{
    xf86AddInputDriver(&EBEAM, module, 0);
    return module;
};

static int _ebeam_open_socket(InputInfoPtr pInfo)
{
    EbeamDevicePtr pEbeam = pInfo->private;

    int initblob[2] = {4 , 3};

    int fd = -1;
    struct sockaddr_in addr;
    struct hostent *host = gethostbyname(pEbeam->host);
    int port = atoi(pEbeam->port);

    if ( host == NULL || port == 0 ) {
        xf86Msg(X_ERROR, "%s: Bad hostname:port : %s:%s.\n", pInfo->name, pEbeam->host, pEbeam->port);
        return fd;
    }

    fd = socket(AF_INET, SOCK_STREAM, PF_UNSPEC);
    if ( fd < 0 )
    {
        xf86Msg(X_ERROR, "%s: failed creating socket.\n", pInfo->name);
    }
    else
    {
        addr.sin_family = AF_INET;
        memcpy(&addr.sin_addr, host->h_addr, host->h_length);
        addr.sin_port = htons(port);
        if ( connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1 )
        {
            xf86Msg(X_ERROR, "%s: failed connecting to %s:%s.\n", pInfo->name, pEbeam->host, pEbeam->port);
            close(fd);
            fd = -1;
        }
        else
        {
            write(fd, &initblob, 2 * sizeof(int));
        }
    }
    return fd;
}

static InputInfoPtr EbeamInit(InputDriverPtr drv,
                                 IDevPtr     dev,
                                 int         flags)
{
    InputInfoPtr   pInfo;
    EbeamDevicePtr pEbeam;
    char           *pointer_name;

    if (!(pInfo = xf86AllocateInput(drv, 0)))
        return NULL;

    pEbeam = xcalloc(1, sizeof(EbeamDeviceRec));
    if (!pEbeam) {
        pInfo->private = NULL;
        xf86DeleteInput(pInfo, 0);
        return NULL;
    }

    pInfo->private = pEbeam;
    pInfo->name = xstrdup(dev->identifier);
    pInfo->flags = 0;
    pInfo->type_name = XI_MOUSE;
    pInfo->conf_idev = dev;
    pInfo->read_input = EbeamReadInput;
    pInfo->switch_mode = NULL;
    pInfo->device_control = EbeamControl;

    /* process driver specific options */
    pEbeam->host = xf86SetStrOption(dev->commonOptions, "Host", "localhost");
    pEbeam->port = xf86SetStrOption(dev->commonOptions, "Port", "7802");
    xf86Msg(X_INFO, "%s: Using ebeam-server at %s:%s.\n", pInfo->name, pEbeam->host, pEbeam->port);

    pointer_name = xf86SetStrOption(dev->commonOptions, "Color", "wand");
    if ( !xf86NameCmp(pointer_name, "red") )
        pEbeam->pointer_id = 1;
    else if ( !xf86NameCmp(pointer_name, "blue") )
        pEbeam->pointer_id = 2;
    else if ( !xf86NameCmp(pointer_name, "green") )
        pEbeam->pointer_id = 3;
    else if ( !xf86NameCmp(pointer_name, "black") )
        pEbeam->pointer_id = 4;
    else if ( !xf86NameCmp(pointer_name, "erase") )
        pEbeam->pointer_id = 5;
    else if ( !xf86NameCmp(pointer_name, "wand") )
        pEbeam->pointer_id = 6;
    else if ( !xf86NameCmp(pointer_name, "any") )
        pEbeam->pointer_id = 7;
    else {
        xf86Msg(X_ERROR, "%s: Unknown pointer name : %s.\n", pInfo->name, pointer_name);
        pEbeam->pointer_id = 7;
    }
    xfree(pointer_name);

    xf86Msg(X_INFO, "%s: Using pointer ID %u.\n", pInfo->name, pEbeam->pointer_id);

    /* process generic options */
    xf86CollectInputOptions(pInfo, NULL, NULL);
    xf86ProcessCommonOptions(pInfo, pInfo->options);

    /* test socket */
    pInfo->fd = _ebeam_open_socket(pInfo);
    if (pInfo->fd < 0)
    {
        xf86Msg(X_ERROR, "%s: No device present, exiting.\n", pInfo->name);
        pInfo->private = NULL;
        xfree(pEbeam);
        xf86DeleteInput(pInfo, 0);
        return NULL;
    }

    close(pInfo->fd);
    pInfo->fd = -1;
    pInfo->flags |= XI86_OPEN_ON_INIT;
    pInfo->flags |= XI86_CONFIGURED;
    return pInfo;
}

static void EbeamUnInit(InputDriverPtr drv,
                        InputInfoPtr   pInfo,
                        int            flags)
{
    EbeamDevicePtr pEbeam = pInfo->private;
    if (pEbeam->host)
    {
        xfree(pEbeam->host);
        pEbeam->host = NULL;
    }
    if (pEbeam->port)
    {
        xfree(pEbeam->port);
        pEbeam->port = NULL;
    }
    /* Common error - pInfo->private must be NULL or valid memoy before
     * passing into xf86DeleteInput */
    pInfo->private = NULL;

    xf86DeleteInput(pInfo, 0);
}

static void _ebeam_fill_labels(DeviceIntPtr device)
{
    InputInfoPtr   pInfo = device->public.devicePrivate;
    EbeamDevicePtr pEbeam = pInfo->private;
    int            i;
    int            ret = Success;

    for ( i = 0; i <  EBEAM_BTNS; i++) {
        pEbeam->btn_map[i + 1] = i + 1; /* default mapping */
        pEbeam->btn_labels[i] = 0;
    }

    for ( i = 0; i < EBEAM_AXES; i++) {
        pEbeam->axis_labels[i] = 0;
    }

#ifdef HAVE_LABELS
    if (EBEAM_BTNS > 0)
        pEbeam->btn_labels[0] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_LEFT);
    if (EBEAM_BTNS > 1)
        pEbeam->btn_labels[1] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_RIGHT);
    if (EBEAM_BTNS > 2) {
        pEbeam->btn_labels[1] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_MIDDLE);
        pEbeam->btn_labels[2] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_RIGHT);
    }

    if (EBEAM_AXES > 0)
        pEbeam->axis_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_X);
    if (EBEAM_AXES > 1)
        pEbeam->axis_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_Y);
#endif
}


static int _ebeam_init_buttons(DeviceIntPtr device)
{
    InputInfoPtr   pInfo = device->public.devicePrivate;
    EbeamDevicePtr pEbeam = pInfo->private;
    int            ret = Success;

    if (!InitButtonClassDeviceStruct(device, EBEAM_BTNS, pEbeam->btn_labels, pEbeam->btn_map)) {
            xf86Msg(X_ERROR, "%s: Failed to register buttons.\n", pInfo->name);
            ret = BadAlloc;
    }
    return ret;
}

static int _ebeam_init_axes(DeviceIntPtr device)
{
    InputInfoPtr   pInfo = device->public.devicePrivate;
    EbeamDevicePtr pEbeam = pInfo->private;
    int            i;

    if (!InitValuatorClassDeviceStruct(device,
                                       EBEAM_AXES,
                                       pEbeam->axis_labels,
                                       GetMotionHistorySize(),
                                       0))
        return BadAlloc;

    pInfo->dev->valuator->mode = Absolute;
    if (!InitAbsoluteClassDeviceStruct(device))
            return BadAlloc;

    for (i = 0; i < EBEAM_AXES; i++) {
            xf86InitValuatorAxisStruct(device, i, pEbeam->axis_labels[i], 0, -1, 1, 0, 1);
            xf86InitValuatorDefaults(device, i);
    }
    return Success;
}

static int EbeamControl(DeviceIntPtr device,
                        int          what)
{
    InputInfoPtr   pInfo  = device->public.devicePrivate;
    EbeamDevicePtr pEbeam = pInfo->private;

    switch(what)
    {
        case DEVICE_INIT:
            _ebeam_fill_labels(device);
            _ebeam_init_buttons(device);
            _ebeam_init_axes(device);
            break;

        /* Switch device on.  Establish socket, start event delivery.  */
        case DEVICE_ON:
            xf86Msg(X_INFO, "%s: On.\n", pInfo->name);
            if (device->public.on)
                    break;

            pInfo->fd = _ebeam_open_socket(pInfo);
            if (pInfo->fd < 0)
            {
                xf86Msg(X_ERROR, "%s: cannot open device.\n", pInfo->name);
                return BadRequest;
            }

            /* xf86FlushInput(pInfo->fd); */
            xf86AddEnabledDevice(pInfo);
            device->public.on = TRUE;
            break;

        /* Switch device off.  Close socket, stop event delivery.  */
       case DEVICE_OFF:
            xf86Msg(X_INFO, "%s: Off.\n", pInfo->name);
            if (!device->public.on)
                break;
            xf86RemoveEnabledDevice(pInfo);
            close(pInfo->fd);
            pInfo->fd = -1;
            device->public.on = FALSE;
            break;

      case DEVICE_CLOSE:
            /* free what we have to free */
            break;
    }
    return Success;
}

/* ebeam-server packets :
 * taille : 256 bytes / 64 int
 * int_0  : 0 : event ; 1 : control ; other : ignore
 *
 * CONTROL PACKET :
 * int_3  : pInfo->flags : 0 : unset XI_ALWAYS_CORE ; 1 : set XI_ALWAYS_CORE
 * int_6  : little button map to ...
 * int_7  : big button map to ...
 * int_8  : tip button map to ...
 * WARNING : ebeam-server always send : little 1 ; big 2 ; tip 4 ; it's up to the driver to map.
 *
 * EVENT PACKET
 * int_3 : pos X
 * int_4 : pos Y
 * int_5 : pointer ID : wand(6) ; red(1) ; blue(2) ; green(3) ; black(4) ; erase(5)
 * int_7 : buttons flags : big 1 | little 2 | tip 4
 */
static void EbeamReadInput(InputInfoPtr pInfo)
{
    EbeamDevicePtr pEbeam = pInfo->private;
    ssize_t len;
    int i, j, b;
    int n;
    int *data;
    int real_btns_state, mapped_btns_state, x, y;

    while(xf86WaitForInput(pInfo->fd, 0) > 0)
    {
        len = read(pInfo->fd, packetbuf, 256 * MAX_PACKET);
        n = len / 256;

        if ( len == 0 || len != n * 256 ) {
            xf86Msg(X_ERROR, "%s : %u bytes read, ignoring bad packet.\n", pInfo->name, len);
            return;
        }

        for ( i = 0; i < n; i++ ) { /* packet */
            data = (int *) &packetbuf[256*i];

            if ( *data == 1 ) { /* control packet */

                /* ALWAYS_CORE flag */
                if ( *(data + 3) )
                    pInfo->flags |= XI86_ALWAYS_CORE;  /* set */
                else
                    pInfo->flags &= ~XI86_ALWAYS_CORE; /* unset*/

                /* hardware buttons mapping */
                if ( *(data + 6) > 0 )          /* Little */
                    pEbeam->btn_map[1] = *(data + 6);
                else
                    pEbeam->btn_map[1] = 0;

                if ( *(data + 7) > 0 )          /* Big */
                    pEbeam->btn_map[2] = *(data + 7);
                else
                    pEbeam->btn_map[2] = 0;

                if ( *(data + 8) > 0 )          /* Tip */
                    pEbeam->btn_map[3] = *(data + 8);
                else
                    pEbeam->btn_map[3] = 0;

                xf86Msg(X_INFO, "%s : Buttons map : Little : %u ; Big : %u ; Tip : %u.\n",
                        pInfo->name, pEbeam->btn_map[1], pEbeam->btn_map[2], pEbeam->btn_map[3]);
            }
            else if ( *data == 0 ) { /* event packet */

                /* pointer ID */
                if ( pEbeam->pointer_id == 7 || *(data + 5) == pEbeam->pointer_id ) {   /* any pointer or good pointer */
                    real_btns_state = *(data + 7);
                    x = *(data + 3);
                    y = *(data + 4);

                    /* Tip pressed : send motion */
                    if ( real_btns_state & TIP_BIT )
                        xf86PostMotionEvent(pInfo->dev, 1, 0, 2, x, y);

                    /* Buttons */
                    mapped_btns_state = 0;

                    /* little */
                    if ( pEbeam->btn_map[1] && (real_btns_state & LIT_BIT) )
                        mapped_btns_state = 1 << (pEbeam->btn_map[1] - 1);
                    /* big */
                    if ( pEbeam->btn_map[2] && (real_btns_state & BIG_BIT) )
                        mapped_btns_state |= 1 << (pEbeam->btn_map[2] - 1);
                    /* tip */
                    if ( pEbeam->btn_map[3] && (real_btns_state & TIP_BIT) )
                        mapped_btns_state |= 1 << (pEbeam->btn_map[3] - 1);

                    if ( mapped_btns_state != pEbeam->previous_mapped_btns_state ) { /* changes occured */
                        for ( b = 0; b < 3; b++ ) {
                            if ( (1 & (mapped_btns_state >> b)) != (1 & (pEbeam->previous_mapped_btns_state >> b)) )
                                xf86PostButtonEvent(pInfo->dev,
                                                    1,
                                                    b+1,
                                                    1 & (mapped_btns_state >> b),
                                                    0,
                                                    2,
                                                    x,
                                                    y);
                        }
                        pEbeam->previous_mapped_btns_state = mapped_btns_state;
                    }
                }
            }
            else {
                xf86Msg(X_ERROR, "%s : Unknown packet type (%u), ignoring.\n", pInfo->name, *data);
            }
        }
    }
}
/*
 * Copyright 2010 Yann Cantin <[email protected]>
 *
 * Parts inspired from Peter Hutterer and Przemysław Firszt "random"
 * driver, logic reverse-engeniered from the old (closed) Luida driver.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without
 * fee, provided that the above copyright notice appear in all copies
 * and that both that copyright notice and this permission notice
 * appear in supporting documentation, and that the name of Red Hat
 * not be used in advertising or publicity pertaining to distribution
 * of the software without specific, written prior permission.  Red
 * Hat makes no representations about the suitability of this software
 * for any purpose.  It is provided "as is" without express or implied
 * warranty.
 *
 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
 * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#define SYSCALL(call) while (((call) == -1) && (errno == EINTR))

#define EBEAM_AXES 2
#define EBEAM_BTNS 3
#define MAX_PACKET 20

#define BIG_BIT 1
#define LIT_BIT 2
#define TIP_BIT 4

typedef struct _EbeamDeviceRec
{
    char  *host;
    char  *port;
    int   pointer_id;
    Atom  axis_labels[EBEAM_AXES];
    CARD8 btn_map[EBEAM_BTNS + 1];      /* 1 :Little ; 2 : Big ; 3 : Tip */
    int   previous_mapped_btns_state;   /* keep state from previous run */
    Atom  btn_labels[EBEAM_BTNS];
} EbeamDeviceRec, *EbeamDevicePtr ;

_______________________________________________
[email protected]: X.Org development
Archives: http://lists.x.org/archives/xorg-devel
Info: http://lists.x.org/mailman/listinfo/xorg-devel

Reply via email to