ecore-drm: Add Ecore_Drm code

@feature: Add Ecore_Drm library code for setting up drm card, outputs,
virtual terminals, framebuffers, and input for use with ecore_evas drm
code.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
This commit is contained in:
Chris Michael 2014-03-06 09:43:48 +00:00
parent 0f1883df9a
commit 3762e85c39
10 changed files with 3378 additions and 0 deletions

View File

@ -0,0 +1,171 @@
#ifndef _ECORE_DRM_H
# define _ECORE_DRM_H
# ifdef EAPI
# undef EAPI
# endif
#ifdef _MSC_VER
# ifdef BUILDING_DLL
# define EAPI __declspec(dllexport)
# else // ifdef BUILDING_DLL
# define EAPI __declspec(dllimport)
# endif // ifdef BUILDING_DLL
#else // ifdef _MSC_VER
# ifdef __GNUC__
# if __GNUC__ >= 4
# define EAPI __attribute__ ((visibility("default")))
# else // if __GNUC__ >= 4
# define EAPI
# endif // if __GNUC__ >= 4
# else // ifdef __GNUC__
# define EAPI
# endif // ifdef __GNUC__
#endif // ifdef _MSC_VER
typedef enum _Ecore_Drm_Op
{
ECORE_DRM_OP_READ_FD_SET,
ECORE_DRM_OP_WRITE_FD_SET,
ECORE_DRM_OP_DEVICE_OPEN,
ECORE_DRM_OP_DEVICE_CLOSE,
ECORE_DRM_OP_DEVICE_MASTER_DROP,
ECORE_DRM_OP_DEVICE_MASTER_SET,
ECORE_DRM_OP_TTY_OPEN,
ECORE_DRM_OP_TTY_CLOSE
} Ecore_Drm_Op;
typedef enum _Ecore_Drm_Op_Result
{
ECORE_DRM_OP_SUCCESS,
ECORE_DRM_OP_FAILURE
} Ecore_Drm_Op_Result;
typedef enum _Ecore_Drm_Evdev_Capabilities
{
EVDEV_KEYBOARD = (1 << 0),
EVDEV_BUTTON = (1 << 1),
EVDEV_MOTION_ABS = (1 << 2),
EVDEV_MOTION_REL = (1 << 3),
EVDEV_TOUCH = (1 << 4),
} Ecore_Drm_Evdev_Capabilities;
typedef enum _Ecore_Drm_Evdev_Event_Type
{
EVDEV_NONE,
EVDEV_ABSOLUTE_TOUCH_DOWN,
EVDEV_ABSOLUTE_MOTION,
EVDEV_ABSOLUTE_TOUCH_UP,
EVDEV_ABSOLUTE_MT_DOWN,
EVDEV_ABSOLUTE_MT_MOTION,
EVDEV_ABSOLUTE_MT_UP,
EVDEV_RELATIVE_MOTION,
} Ecore_Drm_Evdev_Event_Type;
typedef enum _Ecore_Drm_Seat_Capabilities
{
EVDEV_SEAT_POINTER = (1 << 0),
EVDEV_SEAT_KEYBOARD = (1 << 1),
EVDEV_SEAT_TOUCH = (1 << 2),
} Ecore_Drm_Seat_Capabilities;
/* structure for message passing */
typedef struct _Ecore_Drm_Message
{
int opcode, size;
void *data;
} Ecore_Drm_Message;
/* structure for fb objects */
typedef struct _Ecore_Drm_Fb
{
Eina_Bool from_client : 1;
unsigned int id, hdl;
unsigned int stride, size;
int fd;
void *mmap;
/* #ifdef HAVE_GBM */
/* struct gbm_bo *bo; */
/* #endif */
} Ecore_Drm_Fb;
/* opaque structure to represent a drm device */
typedef struct _Ecore_Drm_Device Ecore_Drm_Device;
/* opaque structure to represent a drm output mode */
typedef struct _Ecore_Drm_Output_Mode Ecore_Drm_Output_Mode;
/* opaque structure to represent a drm output */
typedef struct _Ecore_Drm_Output Ecore_Drm_Output;
/* opaque structure to represent a drm udev input */
typedef struct _Ecore_Drm_Input Ecore_Drm_Input;
/* opaque structure to represent a drm evdev input */
typedef struct _Ecore_Drm_Evdev Ecore_Drm_Evdev;
/* opaque structure to represent a drm seat */
typedef struct _Ecore_Drm_Seat Ecore_Drm_Seat;
/* opaque structure to represent a drm sprite */
typedef struct _Ecore_Drm_Sprite Ecore_Drm_Sprite;
/**
* @file
* @brief Ecore functions for dealing with drm, virtual terminals
*
* @defgroup Ecore_Drm_Group Ecore_Drm - Drm Integration
* @ingroup Ecore
*
* Ecore_Drm provides a wrapper and functions for using libdrm
*
* @li @ref Ecore_Drm_Init_Group
* @li @ref Ecore_Drm_Device_Group
* @li @ref Ecore_Drm_Tty_Group
* @li @ref Ecore_Drm_Output_Group
* @li @ref Ecore_Drm_Input_Group
* @li @ref Ecore_Drm_Sprite_Group
*
*/
EAPI int ecore_drm_init(void);
EAPI int ecore_drm_shutdown(void);
/* EAPI void *ecore_drm_gbm_get(Ecore_Drm_Device *dev); */
/* EAPI unsigned int ecore_drm_gbm_format_get(Ecore_Drm_Device *dev); */
EAPI Ecore_Drm_Device *ecore_drm_device_find(const char *name, const char *seat);
EAPI void ecore_drm_device_free(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_device_open(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_device_close(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_device_master_get(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_device_master_set(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_device_master_drop(Ecore_Drm_Device *dev);
EAPI int ecore_drm_device_fd_get(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_tty_open(Ecore_Drm_Device *dev, const char *name);
EAPI Eina_Bool ecore_drm_tty_close(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_tty_release(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_tty_acquire(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_outputs_create(Ecore_Drm_Device *dev);
EAPI void ecore_drm_output_free(Ecore_Drm_Output *output);
EAPI void ecore_drm_output_cursor_size_set(Ecore_Drm_Output *output, int handle, int w, int h);
EAPI Eina_Bool ecore_drm_output_enable(Ecore_Drm_Output *output);
EAPI void ecore_drm_output_fb_release(Ecore_Drm_Output *output, Ecore_Drm_Fb *fb);
EAPI void ecore_drm_output_repaint(Ecore_Drm_Output *output);
EAPI Eina_Bool ecore_drm_inputs_create(Ecore_Drm_Device *dev);
EAPI void ecore_drm_inputs_destroy(Ecore_Drm_Device *dev);
EAPI Eina_Bool ecore_drm_inputs_enable(Ecore_Drm_Input *input);
EAPI void ecore_drm_inputs_disable(Ecore_Drm_Input *input);
EAPI Eina_Bool ecore_drm_sprites_create(Ecore_Drm_Device *dev);
EAPI void ecore_drm_sprites_destroy(Ecore_Drm_Device *dev);
EAPI void ecore_drm_sprites_fb_set(Ecore_Drm_Sprite *sprite, int fb_id, int flags);
EAPI Eina_Bool ecore_drm_sprites_crtc_supported(Ecore_Drm_Output *output, unsigned int supported);
EAPI Ecore_Drm_Fb *ecore_drm_fb_create(Ecore_Drm_Device *dev, int width, int height);
EAPI void ecore_drm_fb_destroy(Ecore_Drm_Fb *fb);
#endif

View File

@ -0,0 +1,414 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "ecore_drm_private.h"
#include <sys/wait.h>
#include <sys/socket.h>
#include <signal.h>
#define RIGHTS_LEN CMSG_LEN(sizeof(int))
#define IOVSET(_iov, _addr, _len) \
(_iov)->iov_base = (void *)(_addr); \
(_iov)->iov_len = (_len);
/* local variables */
static int _ecore_drm_init_count = 0;
static int _ecore_drm_sockets[2] = { -1, -1 };
static struct cmsghdr *cmsgptr = NULL;
/* external variables */
struct udev *udev;
int _ecore_drm_log_dom = -1;
#ifdef LOG_TO_FILE
FILE *lg;
#endif
static Eina_Bool
_ecore_drm_sockets_create(void)
{
if (_ecore_drm_sockets[0] > -1) return EINA_TRUE;
/* create a pair of sequenced sockets (fixed-length)
* NB: when reading from one of these, it is required that we read
* an entire packet with each read() call */
if (socketpair(AF_LOCAL, SOCK_SEQPACKET | SOCK_NONBLOCK,
0, _ecore_drm_sockets) < 0)
{
ERR("Socketpair Failed: %m");
return EINA_FALSE;
}
/* NB: We don't want cloexec for the sockets. That would cause them to be
* closed when we exec the child process but we need them open so that
* we can pass messages */
/* if (fcntl(_ecore_drm_sockets[0], F_SETFD, FD_CLOEXEC) < 0) */
/* { */
/* ERR("Failed to set CLOEXEC: %m"); */
/* return EINA_FALSE; */
/* } */
/* DBG("Parent Socket: %d", _ecore_drm_sockets[0]); */
/* DBG("Child Socket: %d", _ecore_drm_sockets[1]); */
return EINA_TRUE;
}
static Eina_Bool
_ecore_drm_launcher_spawn(void)
{
pid_t pid;
if ((pid = fork()) < 0) return EINA_FALSE;
if (pid == 0)
{
char renv[64], wenv[64], buff[PATH_MAX];
char *args[1] = { NULL };
sigset_t mask;
/* read socket for slave is 1 */
snprintf(renv, sizeof(renv), "ECORE_DRM_LAUNCHER_SOCKET_READ=%d",
_ecore_drm_sockets[1]);
/* write socket for slave is 0 */
snprintf(wenv, sizeof(wenv), "ECORE_DRM_LAUNCHER_SOCKET_WRITE=%d",
_ecore_drm_sockets[0]);
/* assemble exec path */
snprintf(buff, sizeof(buff),
"%s/ecore_drm/bin/%s/ecore_drm_launch",
PACKAGE_LIB_DIR, MODULE_ARCH);
/* don't give our signal mask to the child */
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGCHLD);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTTIN);
sigaddset(&mask, SIGTTOU);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
/* NB: We need to use execve here so that capabilities are inherited.
* Also, this should set Our (ecore_drm) effective uid to be the
* owner of the launched process (setuid in this case) */
char *ev[3] = { strdup(renv), strdup(wenv), NULL };
execve(buff, args, ev);
}
else
{
int status;
while (waitpid(pid, &status, WNOHANG) < 0)
if (errno != EINTR) break;
return EINA_TRUE;
}
return EINA_FALSE;
}
static ssize_t
_ecore_drm_socket_send(int opcode, int fd, void *data, size_t bytes)
{
Ecore_Drm_Message dmsg;
struct iovec iov[2];
struct msghdr msg;
ssize_t size;
/* Simplified version of sending messages. We don't need to send any
* 'credentials' with this as it is just basically an IPC to send over
* our request to the slave process */
/* NB: Hmm, don't think we need to set any socket options here */
memset(&dmsg, 0, sizeof(dmsg));
IOVSET(iov + 0, &dmsg, sizeof(dmsg));
IOVSET(iov + 1, &data, bytes);
dmsg.opcode = opcode;
dmsg.size = bytes;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
msg.msg_flags = 0;
if ((!cmsgptr) && (!(cmsgptr = malloc(RIGHTS_LEN))))
return -1;
cmsgptr->cmsg_level = SOL_SOCKET;
cmsgptr->cmsg_type = SCM_RIGHTS;
cmsgptr->cmsg_len = RIGHTS_LEN;
msg.msg_control = cmsgptr;
msg.msg_controllen = RIGHTS_LEN;
if (fd > -1)
*((int *)CMSG_DATA(cmsgptr)) = fd;
else
*((int *)CMSG_DATA(cmsgptr)) = _ecore_drm_sockets[1];
errno = 0;
size = sendmsg(_ecore_drm_sockets[1], &msg, MSG_EOR);
if (errno != 0)
{
DBG("Error Sending Message: %m");
}
/* DBG("Sent %li bytes to Socket %d", size, _ecore_drm_sockets[1]); */
return size;
}
static int
_ecore_drm_socket_receive(int opcode EINA_UNUSED, int *fd, void **data, size_t bytes)
{
int ret = ECORE_DRM_OP_FAILURE;
Ecore_Drm_Message dmsg;
struct cmsghdr *cmsg;
struct iovec iov[2];
struct msghdr msg;
char buff[CMSG_SPACE(sizeof(fd))];
/* ssize_t size; */
memset(&dmsg, 0, sizeof(dmsg));
memset(&buff, 0, sizeof(buff));
IOVSET(iov + 0, &dmsg, sizeof(dmsg));
IOVSET(iov + 1, &buff, sizeof(buff));
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
msg.msg_flags = 0;
if ((!cmsgptr) && (!(cmsgptr = malloc(RIGHTS_LEN))))
return -1;
msg.msg_control = cmsgptr;
msg.msg_controllen = RIGHTS_LEN;
errno = 0;
recvmsg(_ecore_drm_sockets[0], &msg, 0);
if (errno != 0)
{
ERR("Failed to receive message: %m");
return -1;
}
/* DBG("Received %li bytes from %d", size, _ecore_drm_sockets[0]); */
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg))
{
if (cmsg->cmsg_level != SOL_SOCKET)
continue;
switch (cmsg->cmsg_type)
{
case SCM_RIGHTS:
if (fd) *fd = *((int *)CMSG_DATA(cmsg));
switch (dmsg.opcode)
{
case ECORE_DRM_OP_DEVICE_OPEN:
case ECORE_DRM_OP_DEVICE_CLOSE:
case ECORE_DRM_OP_TTY_OPEN:
case ECORE_DRM_OP_DEVICE_MASTER_DROP:
case ECORE_DRM_OP_DEVICE_MASTER_SET:
if ((fd) && (*fd >= 0)) ret = ECORE_DRM_OP_SUCCESS;
if (data) memcpy(*data, buff, bytes);
break;
default:
break;
}
break;
default:
break;
}
}
return ret;
}
void
_ecore_drm_message_send(int opcode, int fd, void *data, size_t bytes)
{
_ecore_drm_socket_send(opcode, fd, data, bytes);
}
Eina_Bool
_ecore_drm_message_receive(int opcode, int *fd, void **data, size_t bytes)
{
int ret = ECORE_DRM_OP_FAILURE;
ret = _ecore_drm_socket_receive(opcode, fd, data, bytes);
if (ret != ECORE_DRM_OP_SUCCESS) return EINA_FALSE;
return EINA_TRUE;
}
/**
* @defgroup Ecore_Drm_Init_Group Drm Library Init and Shutdown Functions
*
* Functions that start and shutdown the Ecore_Drm Library.
*/
/**
* Initialize the Ecore_Drm library
*
* @return The number of times the library has been initialized without
* being shut down. 0 is returned if an error occurs.
*
* @ingroup Ecore_Drm_Init_Group
*/
EAPI int
ecore_drm_init(void)
{
/* if we have already initialized, return the count */
if (++_ecore_drm_init_count != 1) return _ecore_drm_init_count;
/* try to init eina */
if (!eina_init()) return --_ecore_drm_init_count;
if (!ecore_init())
{
eina_shutdown();
return --_ecore_drm_init_count;
}
/* set logging level */
eina_log_level_set(EINA_LOG_LEVEL_DBG);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
/* optionally log output to a file */
#ifdef LOG_TO_FILE
int log_fd;
char log_path[PATH_MAX];
mode_t um;
/* assemble logging file path */
strcpy(log_path, "/tmp/ecore_drm_XXXXXX");
/* create temporary logging file */
um = umask(S_IRWXG | S_IRWXO);
log_fd = mkstemp(log_path);
umask(um);
/* try to open logging file */
if (!(lg = fdopen(log_fd, "w")))
goto log_err;
eina_log_print_cb_set(eina_log_print_cb_file, lg);
#endif
/* try to create logging domain */
_ecore_drm_log_dom =
eina_log_domain_register("ecore_drm", ECORE_DRM_DEFAULT_LOG_COLOR);
if (!_ecore_drm_log_dom)
{
EINA_LOG_ERR("Could not create log domain for Ecore_Drm");
goto log_err;
}
/* set default logging level for this domain */
if (!eina_log_domain_level_check(_ecore_drm_log_dom, EINA_LOG_LEVEL_DBG))
eina_log_domain_level_set("ecore_drm", EINA_LOG_LEVEL_DBG);
/* try to init udev */
if (!(udev = udev_new()))
goto udev_err;
/* try to create the socketpair */
if (!_ecore_drm_sockets_create())
goto sock_err;
/* try to run Spartacus */
if (!_ecore_drm_launcher_spawn())
goto spawn_err;
/* return init count */
return _ecore_drm_init_count;
spawn_err:
close(_ecore_drm_sockets[0]);
close(_ecore_drm_sockets[1]);
sock_err:
if (udev) udev_unref(udev);
udev_err:
ecore_shutdown();
eina_log_domain_unregister(_ecore_drm_log_dom);
_ecore_drm_log_dom = -1;
log_err:
#ifdef LOG_TO_FILE
if (lg) fclose(lg);
#endif
/* shutdown eina */
eina_shutdown();
return --_ecore_drm_init_count;
}
/**
* Shutdown the Ecore_Drm library.
*
* @return The number of times the library has been initialized without
* being shutdown. 0 is returned if an error occurs.
*
* @ingroup Ecore_Drm_Init_Group
*/
EAPI int
ecore_drm_shutdown(void)
{
/* if we are still in use, decrement init count and get out */
if (--_ecore_drm_init_count != 0) return _ecore_drm_init_count;
/* close udev handle */
if (udev) udev_unref(udev);
/* close sockets */
close(_ecore_drm_sockets[0]);
close(_ecore_drm_sockets[1]);
ecore_shutdown();
/* unregsiter log domain */
eina_log_domain_unregister(_ecore_drm_log_dom);
_ecore_drm_log_dom = -1;
#ifdef LOG_TO_FILE
if (lg) fclose(lg);
#endif
/* shutdown eina */
eina_shutdown();
/* return init count */
return _ecore_drm_init_count;
}
EAPI void *
ecore_drm_gbm_get(Ecore_Drm_Device *dev)
{
if (!dev) return NULL;
#ifdef HAVE_GBM
return dev->gbm;
#endif
return NULL;
}
EAPI unsigned int
ecore_drm_gbm_format_get(Ecore_Drm_Device *dev)
{
if (!dev) return 0;
return dev->format;
}

View File

@ -0,0 +1,583 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "ecore_drm_private.h"
#include <dlfcn.h>
/* #ifdef HAVE_GBM */
/* static Eina_Bool */
/* _ecore_drm_device_egl_config_get(Ecore_Drm_Device *dev, const EGLint *attribs, const EGLint *visual) */
/* { */
/* EGLint c = 0, m = 0; */
/* EGLConfig *cfgs; */
/* int i = 0; */
/* if (!eglGetConfigs(dev->egl.disp, NULL, 0, &c) || (c < 1)) */
/* return EINA_FALSE; */
/* if (!(cfgs = calloc(c, sizeof(*cfgs)))) return EINA_FALSE; */
/* if (!eglChooseConfig(dev->egl.disp, attribs, cfgs, c, &m)) */
/* { */
/* free(cfgs); */
/* return EINA_FALSE; */
/* } */
/* for (i = 0; i < m; i++) */
/* { */
/* EGLint id; */
/* if (visual) */
/* { */
/* if (!eglGetConfigAttrib(dev->egl.disp, cfgs[i], */
/* EGL_NATIVE_VISUAL_ID, &id)) */
/* continue; */
/* if ((id != 0) && (id != *visual)) */
/* continue; */
/* } */
/* dev->egl.cfg = cfgs[i]; */
/* free(cfgs); */
/* return EINA_TRUE; */
/* } */
/* free(cfgs); */
/* return EINA_FALSE; */
/* } */
/* #endif */
static void
_ecore_drm_device_cb_page_flip(int fd EINA_UNUSED, unsigned int frame EINA_UNUSED, unsigned int sec EINA_UNUSED, unsigned int usec EINA_UNUSED, void *data)
{
Ecore_Drm_Output *output;
DBG("Drm Page Flip Event");
if (!(output = data)) return;
if (output->pending_flip)
{
ecore_drm_output_fb_release(output, output->current);
output->current = output->next;
output->next = NULL;
}
output->pending_flip = EINA_FALSE;
if (!output->pending_vblank) ecore_drm_output_repaint(output);
}
static void
_ecore_drm_device_cb_vblank(int fd EINA_UNUSED, unsigned int frame EINA_UNUSED, unsigned int sec EINA_UNUSED, unsigned int usec EINA_UNUSED, void *data)
{
Ecore_Drm_Sprite *sprite;
Ecore_Drm_Output *output;
DBG("Drm VBlank Event");
if (!(sprite = data)) return;
output = sprite->output;
output->pending_vblank = EINA_FALSE;
ecore_drm_output_fb_release(output, sprite->current_fb);
sprite->current_fb = sprite->next_fb;
sprite->next_fb = NULL;
if (!output->pending_flip) _ecore_drm_output_frame_finish(output);
}
static Eina_Bool
_ecore_drm_device_cb_event(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED)
{
Ecore_Drm_Device *dev;
drmEventContext ctx;
if (!(dev = data)) return ECORE_CALLBACK_RENEW;
DBG("Drm Device Event");
memset(&ctx, 0, sizeof(ctx));
ctx.version = DRM_EVENT_CONTEXT_VERSION;
ctx.page_flip_handler = _ecore_drm_device_cb_page_flip;
ctx.vblank_handler = _ecore_drm_device_cb_vblank;
drmHandleEvent(dev->drm.fd, &ctx);
return ECORE_CALLBACK_RENEW;
}
static Eina_Bool
_ecore_drm_device_cb_idle(void *data)
{
Ecore_Drm_Device *dev;
Ecore_Drm_Output *output;
Eina_List *l;
if (!(dev = data)) return ECORE_CALLBACK_CANCEL;
EINA_LIST_FOREACH(dev->outputs, l, output)
{
output->need_repaint = EINA_TRUE;
if (output->repaint_scheduled) continue;
_ecore_drm_output_repaint_start(output);
}
return ECORE_CALLBACK_RENEW;
}
/**
* @defgroup Ecore_Drm_Device_Group Device manipulation functions
*
* Functions that deal with finding, opening, closing, and otherwise using
* the DRM device itself.
*/
/**
* Find a drm device in the system.
*
* @param name The name of the device to find. If NULL, this function will
* search for the default drm device.
* @param seat The name of the seat where this device may be found. If NULL,
* this function will use a default seat name 'seat0'.
*
* @return An opaque Ecore_Drm_Device structure representing the card.
*
* @ingroup Ecore_Drm_Device_Group
*/
EAPI Ecore_Drm_Device *
ecore_drm_device_find(const char *name, const char *seat)
{
Ecore_Drm_Device *dev = NULL;
struct udev_enumerate *uenum;
struct udev_list_entry *uentry;
struct udev_device *udevice, *tmpdevice = NULL, *pcidevice;
const char *path = NULL, *devseat = NULL;
/* check for existing udev reference */
if (!udev) return NULL;
/* setup udev enumerator */
uenum = udev_enumerate_new(udev);
udev_enumerate_add_match_subsystem(uenum, "drm");
udev_enumerate_add_match_subsystem(uenum, "card[0-9]*");
/* ask udev for list of drm devices */
udev_enumerate_scan_devices(uenum);
/* loop list of returned devices */
udev_list_entry_foreach(uentry, udev_enumerate_get_list_entry(uenum))
{
/* get device path */
path = udev_list_entry_get_name(uentry);
/* get udev device */
if (!(udevice = udev_device_new_from_syspath(udev, path)))
continue;
/* if we are looking for a certain device, then compare names */
if (name)
{
if (strcmp(name, udev_device_get_devnode(udevice)))
{
udev_device_unref(udevice);
continue;
}
}
/* get this devices' seat */
devseat = udev_device_get_property_value(udevice, "ID_SEAT");
if (!devseat) devseat = "seat0";
/* if we are looking for a device on a certain seat, compare it */
if (seat)
{
if (strcmp(seat, devseat))
{
udev_device_unref(udevice);
continue;
}
}
else
{
/* no seat name passed to use. check default */
if (strcmp(devseat, "seat0"))
{
udev_device_unref(udevice);
continue;
}
}
/* try to find the boot_vga attribute */
if ((pcidevice =
udev_device_get_parent_with_subsystem_devtype(udevice, "pci", NULL)))
{
const char *id;
if ((id = udev_device_get_sysattr_value(pcidevice, "boot_vga")))
{
if (!strcmp(id, "1"))
{
if (tmpdevice) udev_device_unref(tmpdevice);
tmpdevice = udevice;
break;
}
}
}
if (!tmpdevice)
tmpdevice = udevice;
else
udev_device_unref(udevice);
}
/* destroy the enumerator */
udev_enumerate_unref(uenum);
if (tmpdevice)
{
DBG("Found Drm Device");
DBG("\tFilename: %s", udev_device_get_devnode(tmpdevice));
DBG("\tDriver: %s", udev_device_get_driver(tmpdevice));
DBG("\tDevpath: %s", udev_device_get_devpath(tmpdevice));
DBG("\tSyspath: %s", udev_device_get_syspath(tmpdevice));
DBG("\tSysname: %s", udev_device_get_sysname(tmpdevice));
/* try to allocate space for return device structure */
if ((dev = calloc(1, sizeof(Ecore_Drm_Device))))
{
const char *id, *seat;
/* set device name */
dev->drm.name =
eina_stringshare_add(udev_device_get_devnode(tmpdevice));
/* set device path */
dev->drm.path =
eina_stringshare_add(udev_device_get_syspath(tmpdevice));
/* store id for this device */
if ((id = udev_device_get_sysnum(tmpdevice)))
dev->id = atoi(id);
/* set dev seat */
seat = udev_device_get_property_value(tmpdevice, "ID_SEAT");
if (!seat) seat = "seat0";
dev->seat = eina_stringshare_add(seat);
dev->format = GBM_FORMAT_XRGB8888;
dev->use_hw_accel = EINA_FALSE;
}
}
/* release device reference */
udev_device_unref(tmpdevice);
return dev;
}
/**
* Free an Ecore_Drm_Device
*
* This function will cleanup and free any previously allocated Ecore_Drm_Device.
*
* @param dev The Ecore_Drm_Device to free
*
* @ingroup Ecore_Drm_Device_Group
*/
EAPI void
ecore_drm_device_free(Ecore_Drm_Device *dev)
{
Ecore_Drm_Output *output;
/* check for valid device */
if (!dev) return;
/* free outputs */
EINA_LIST_FREE(dev->outputs, output)
ecore_drm_output_free(output);
/* free crtcs */
if (dev->crtcs) free(dev->crtcs);
/* free device name */
if (dev->drm.name) eina_stringshare_del(dev->drm.name);
/* free device path */
if (dev->drm.path) eina_stringshare_del(dev->drm.path);
/* free device seat */
if (dev->seat) eina_stringshare_del(dev->seat);
/* free structure */
free(dev);
}
/**
* Open an Ecore_Drm_Device
*
* This function will open an existing Ecore_Drm_Device for use.
*
* @param dev The Ecore_Drm_Device to try and open
*
* @return EINA_TRUE on success, EINA_FALSE on failure
*
* @ingroup Ecore_Drm_Device_Group
*/
EAPI Eina_Bool
ecore_drm_device_open(Ecore_Drm_Device *dev)
{
uint64_t caps;
/* check for valid device */
if ((!dev) || (!dev->drm.name)) return EINA_FALSE;
dev->drm.fd = open(dev->drm.name, O_RDWR);
if (dev->drm.fd < 0) return EINA_FALSE;
DBG("Opened Device %s : %d", dev->drm.name, dev->drm.fd);
if (!drmGetCap(dev->drm.fd, DRM_CAP_TIMESTAMP_MONOTONIC, &caps))
{
if (caps == 1)
dev->drm.clock = CLOCK_MONOTONIC;
else
dev->drm.clock = CLOCK_REALTIME;
}
else
{
ERR("Could not get device capabilities: %m");
}
/* #ifdef HAVE_GBM */
/* if (getenv("ECORE_DRM_HW_ACCEL")) */
/* { */
/* Typically, gbm loads the dri driver However some versions of Mesa
* do not have libglapi symbols linked in the driver. Because of this,
* using hardware accel for our drm code Could fail with a
* message that the driver could not load. Let's be proactive and
* work around this for the user by preloading the glapi library */
/* dlopen("libglapi.so.0", (RTLD_LAZY | RTLD_GLOBAL)); */
/* if ((dev->gbm = gbm_create_device(dev->drm.fd))) */
/* { */
/* EGLint major, minor, visual; */
/* const EGLint attribs[] = */
/* { */
/* EGL_SURFACE_TYPE, EGL_WINDOW_BIT, */
/* EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, */
/* EGL_BLUE_SIZE, 1, EGL_ALPHA_SIZE, 0, */
/* EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE */
/* }; */
/* dev->use_hw_accel = EINA_TRUE; */
/* dev->format = GBM_FORMAT_XRGB8888; */
/* dev->egl.disp = eglGetDisplay(dev->gbm); */
/* if (dev->egl.disp == EGL_NO_DISPLAY) */
/* { */
/* ERR("Could not get egl display"); */
/* goto init_software; */
/* } */
/* if (!eglInitialize(dev->egl.disp, &major, &minor)) */
/* { */
/* ERR("Could not initialize egl"); */
/* goto init_software; */
/* } */
/* visual = dev->format; */
/* if (!_ecore_drm_device_egl_config_get(dev, attribs, &visual)) */
/* { */
/* ERR("Could not get egl config"); */
/* goto init_software; */
/* } */
/* } */
/* else */
/* { */
/* WRN("Failed to create gbm device"); */
/* goto init_software; */
/* } */
/* } */
/* else */
/* #endif */
/* { */
/* TODO: init software */
/* init_software: */
/* DBG("Init Software Engine"); */
/* #ifdef HAVE_GBM */
/* if (dev->egl.disp) */
/* { */
/* eglMakeCurrent(dev->egl.disp, EGL_NO_SURFACE, EGL_NO_SURFACE, */
/* EGL_NO_CONTEXT); */
/* eglTerminate(dev->egl.disp); */
/* eglReleaseThread(); */
/* } */
/* if (dev->gbm) gbm_device_destroy(dev->gbm); */
/* dev->gbm = NULL; */
/* #endif */
/* } */
dev->drm.hdlr =
ecore_main_fd_handler_add(dev->drm.fd, ECORE_FD_READ,
_ecore_drm_device_cb_event, dev, NULL, NULL);
dev->drm.idler =
ecore_idle_enterer_add(_ecore_drm_device_cb_idle, dev);
return EINA_TRUE;
}
/**
* Close an Ecore_Drm_Device
*
* This function will close a previously opened Ecore_Drm_Device
*
* @param dev The Ecore_Drm_Device to free
*
* @return EINA_TRUE on success, EINA_FALSE on failure
*
* @ingroup Ecore_Drm_Device_Group
*/
EAPI Eina_Bool
ecore_drm_device_close(Ecore_Drm_Device *dev)
{
/* check for valid device */
if (!dev) return EINA_FALSE;
/* #ifdef HAVE_GBM */
/* if (dev->use_hw_accel) */
/* { */
/* if (dev->egl.disp) */
/* { */
/* eglMakeCurrent(dev->egl.disp, EGL_NO_SURFACE, EGL_NO_SURFACE, */
/* EGL_NO_CONTEXT); */
/* eglTerminate(dev->egl.disp); */
/* eglReleaseThread(); */
/* } */
/* if (dev->gbm) gbm_device_destroy(dev->gbm); */
/* dev->gbm = NULL; */
/* } */
/* #endif */
if (dev->drm.hdlr) ecore_main_fd_handler_del(dev->drm.hdlr);
dev->drm.hdlr = NULL;
close(dev->drm.fd);
/* reset device fd */
dev->drm.fd = -1;
/* free(data); */
return EINA_TRUE;
}
/**
* Get if a given Ecore_Drm_Device is master
*
* This function will check if the given drm device is set to master
*
* @param dev The Ecore_Drm_Device to check
*
* @return EINA_TRUE if device is master, EINA_FALSE otherwise
*
* @ingroup Ecore_Drm_Device_Group
*/
EAPI Eina_Bool
ecore_drm_device_master_get(Ecore_Drm_Device *dev)
{
/* drm_magic_t mag; */
/* check for valid device */
if ((!dev) || (dev->drm.fd < 0)) return EINA_FALSE;
/* FIXME: Remote this to the slave process !! */
/* get if we are master or not */
/* if ((drmGetMagic(dev->drm.fd, &mag) == 0) && */
/* (drmAuthMagic(dev->drm.fd, mag) == 0)) */
/* return EINA_TRUE; */
return EINA_FALSE;
}
/**
* Set a given Ecore_Drm_Device to master
*
* This function will attempt to set a given drm device to be master
*
* @param dev The Ecore_Drm_Device to set
*
* @return EINA_TRUE on success, EINA_FALSE on failure
*
* @ingroup Ecore_Drm_Device_Group
*/
EAPI Eina_Bool
ecore_drm_device_master_set(Ecore_Drm_Device *dev)
{
Eina_Bool ret = EINA_FALSE;
int dfd;
/* check for valid device */
if ((!dev) || (dev->drm.fd < 0)) return EINA_FALSE;
DBG("Set Master On Fd: %d", dev->drm.fd);
/* try to close the device */
_ecore_drm_message_send(ECORE_DRM_OP_DEVICE_MASTER_SET, dev->drm.fd,
NULL, 0);
/* get the result of the close operation */
ret = _ecore_drm_message_receive(ECORE_DRM_OP_DEVICE_MASTER_SET, &dfd,
NULL, 0);
if (!ret) return EINA_FALSE;
return EINA_TRUE;
}
/**
* Tell a given Ecore_Drm_Device to stop being master
*
* This function will attempt to ask a drm device to stop being master
*
* @param dev The Ecore_Drm_Device to set
*
* @return EINA_TRUE on success, EINA_FALSE on failure
*
* @ingroup Ecore_Drm_Device_Group
*/
EAPI Eina_Bool
ecore_drm_device_master_drop(Ecore_Drm_Device *dev)
{
Eina_Bool ret = EINA_FALSE;
int dfd;
/* check for valid device */
if ((!dev) || (dev->drm.fd < 0)) return EINA_FALSE;
DBG("Drop Master On Fd: %d", dev->drm.fd);
/* try to close the device */
_ecore_drm_message_send(ECORE_DRM_OP_DEVICE_MASTER_DROP, dev->drm.fd,
NULL, 0);
/* get the result of the close operation */
ret = _ecore_drm_message_receive(ECORE_DRM_OP_DEVICE_MASTER_DROP, &dfd,
NULL, 0);
if (!ret) return EINA_FALSE;
return EINA_TRUE;
}
EAPI int
ecore_drm_device_fd_get(Ecore_Drm_Device *dev)
{
if (!dev) return -1;
return dev->drm.fd;
}

View File

@ -0,0 +1,360 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
/* copied from udev/extras/input_id/input_id.c */
/* we must use this kernel-compatible implementation */
#define BITS_PER_LONG (sizeof(unsigned long) * 8)
#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
#define OFF(x) ((x)%BITS_PER_LONG)
#define BIT(x) (1UL<<OFF(x))
#define LONG(x) ((x)/BITS_PER_LONG)
#define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1)
/* end copied */
#include "ecore_drm_private.h"
#include <sys/ioctl.h>
#include <linux/input.h>
/* local functions */
static Eina_Bool
_device_configure(Ecore_Drm_Evdev *edev)
{
Eina_Bool ret = EINA_FALSE;
if (!edev) return EINA_FALSE;
if ((edev->caps & (EVDEV_MOTION_ABS | EVDEV_MOTION_REL)) &&
(edev->caps & EVDEV_BUTTON))
{
DBG("Input device %s is a pointer", edev->name);
edev->seat_caps |= EVDEV_SEAT_POINTER;
ret = EINA_TRUE;
}
if (edev->caps & EVDEV_KEYBOARD)
{
DBG("Input device %s is a keyboard", edev->name);
edev->seat_caps |= EVDEV_SEAT_KEYBOARD;
ret = EINA_TRUE;
}
if (edev->caps & EVDEV_TOUCH)
{
DBG("Input device %s is a touchpad", edev->name);
edev->seat_caps |= EVDEV_SEAT_TOUCH;
ret = EINA_TRUE;
}
return ret;
}
static Eina_Bool
_device_handle(Ecore_Drm_Evdev *edev)
{
struct input_absinfo absinfo;
unsigned long dev_bits[NBITS(EV_MAX)];
unsigned long abs_bits[NBITS(ABS_MAX)];
unsigned long rel_bits[NBITS(REL_MAX)];
unsigned long key_bits[NBITS(KEY_MAX)];
/* Eina_Bool have_key = EINA_FALSE; */
Eina_Bool have_abs = EINA_FALSE;
if (!edev) return EINA_FALSE;
ioctl(edev->fd, EVIOCGBIT(0, sizeof(dev_bits)), dev_bits);
if (TEST_BIT(dev_bits, EV_ABS))
{
have_abs = EINA_TRUE;
ioctl(edev->fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)), abs_bits);
if ((TEST_BIT(abs_bits, ABS_WHEEL)) ||
(TEST_BIT(abs_bits, ABS_GAS)) ||
(TEST_BIT(abs_bits, ABS_BRAKE)) ||
(TEST_BIT(abs_bits, ABS_HAT0X)))
{
/* ignore joystick */
return EINA_FALSE;
}
if (TEST_BIT(abs_bits, ABS_X))
{
ioctl(edev->fd, EVIOCGABS(ABS_X), &absinfo);
edev->abs.min_x = absinfo.minimum;
edev->abs.max_x = absinfo.maximum;
edev->caps |= EVDEV_MOTION_ABS;
}
if (TEST_BIT(abs_bits, ABS_Y))
{
ioctl(edev->fd, EVIOCGABS(ABS_Y), &absinfo);
edev->abs.min_y = absinfo.minimum;
edev->abs.max_y = absinfo.maximum;
edev->caps |= EVDEV_MOTION_ABS;
}
if ((TEST_BIT(abs_bits, ABS_MT_POSITION_X)) &&
(TEST_BIT(abs_bits, ABS_MT_POSITION_Y)))
{
DBG("Handle MultiTouch Device: %s", edev->path);
}
}
if (TEST_BIT(dev_bits, EV_REL))
{
ioctl(edev->fd, EVIOCGBIT(EV_REL, sizeof(rel_bits)), rel_bits);
if ((TEST_BIT(rel_bits, REL_X)) || (TEST_BIT(rel_bits, REL_Y)))
edev->caps |= EVDEV_MOTION_REL;
}
if (TEST_BIT(dev_bits, EV_KEY))
{
unsigned int i = 0;
/* have_key = EINA_TRUE; */
ioctl(edev->fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), key_bits);
if (have_abs)
{
if ((TEST_BIT(key_bits, BTN_TOOL_FINGER)) &&
(!TEST_BIT(key_bits, BTN_TOOL_PEN)))
{
DBG("Device Is Touchpad: %s", edev->path);
}
}
for (i = KEY_ESC; i < KEY_MAX; i++)
{
if ((i >= BTN_MISC) && (i < KEY_OK)) continue;
if (TEST_BIT(key_bits, i))
{
edev->caps |= EVDEV_KEYBOARD;
break;
}
}
if (TEST_BIT(key_bits, BTN_TOUCH))
edev->caps |= EVDEV_TOUCH;
for (i = BTN_MISC; i < BTN_JOYSTICK; i++)
{
if (TEST_BIT(key_bits, i))
{
edev->caps |= EVDEV_BUTTON;
edev->caps &= ~EVDEV_TOUCH;
break;
}
}
}
if (TEST_BIT(dev_bits, EV_LED)) edev->caps |= EVDEV_KEYBOARD;
return EINA_TRUE;
}
static void
_device_notify_key(Ecore_Drm_Evdev *dev, struct input_event *event, unsigned int timestamp)
{
DBG("Key Event");
DBG("\tCode: %d", event->code);
DBG("\tValue: %d", event->value);
if ((event->code >= KEY_ESC) && (event->code <= KEY_COMPOSE))
{
/* ignore key repeat */
if (event->value == 2)
{
DBG("\tKey Repeat");
}
}
}
static void
_device_process_flush(Ecore_Drm_Evdev *dev, unsigned int timestamp)
{
switch (dev->pending_event)
{
case EVDEV_NONE:
return;
case EVDEV_RELATIVE_MOTION:
goto out;
break;
case EVDEV_ABSOLUTE_MT_DOWN:
goto out;
break;
case EVDEV_ABSOLUTE_MT_MOTION:
goto out;
break;
case EVDEV_ABSOLUTE_MT_UP:
goto out;
break;
case EVDEV_ABSOLUTE_TOUCH_DOWN:
goto out;
break;
case EVDEV_ABSOLUTE_MOTION:
goto out;
break;
case EVDEV_ABSOLUTE_TOUCH_UP:
goto out;
break;
}
out:
dev->pending_event = EVDEV_NONE;
}
static void
_device_process_key(Ecore_Drm_Evdev *dev, struct input_event *event, unsigned int timestamp)
{
if (event->code == BTN_TOUCH)
{
/* TODO: check for mt device */
return;
}
_device_process_flush(dev, timestamp);
switch (event->code)
{
case BTN_LEFT:
case BTN_RIGHT:
case BTN_MIDDLE:
case BTN_SIDE:
case BTN_EXTRA:
case BTN_FORWARD:
case BTN_BACK:
case BTN_TASK:
/* TODO: notify button */
break;
default:
_device_notify_key(dev, event, timestamp);
break;
}
}
static void
_device_process(Ecore_Drm_Evdev *dev, struct input_event *event, int count)
{
struct input_event *ev, *end;
unsigned int timestamp = 0;
DBG("Evdev Device Process");
ev = event;
end = ev + count;
for (ev = event; ev < end; ev++)
{
timestamp = (ev->time.tv_sec * 1000) + (ev->time.tv_usec / 1000);
switch (ev->type)
{
case EV_KEY:
_device_process_key(dev, ev, timestamp);
break;
case EV_REL:
DBG("\tRelative Event");
break;
case EV_ABS:
DBG("\tAbsolute Event");
break;
case EV_SYN:
_device_process_flush(dev, timestamp);
break;
default:
break;
}
}
}
static Eina_Bool
_cb_device_data(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED)
{
Ecore_Drm_Evdev *edev;
struct input_event ev[32];
int len = 0;
if (!(edev = data)) return EINA_TRUE;
do
{
len = read(edev->fd, &ev, sizeof(ev));
if ((len < 0) || ((len % sizeof(ev[0])) != 0))
{
if ((len < 0) && (errno != EAGAIN) && (errno != EINTR))
{
ERR("Device Died");
}
return EINA_TRUE;
}
edev->event_process(edev, ev, (len / sizeof(ev[0])));
} while (len > 0);
return EINA_TRUE;
}
/* external functions */
Ecore_Drm_Evdev *
_ecore_drm_evdev_device_create(Ecore_Drm_Seat *seat, const char *path, int fd)
{
Ecore_Drm_Evdev *edev;
char name[256] = "unknown";
if (!(edev = calloc(1, sizeof(Ecore_Drm_Evdev))))
return NULL;
edev->seat = seat;
edev->path = eina_stringshare_add(path);
edev->fd = fd;
if (ioctl(edev->fd, EVIOCGNAME(sizeof(name)), name) < 0)
DBG("Error getting device name: %m");
name[sizeof(name) - 1] = '\0';
edev->name = eina_stringshare_add(name);
if (!_device_handle(edev))
{
ERR("Unhandled Input Device: %s", name);
_ecore_drm_evdev_device_destroy(edev);
return NULL;
}
if (!_device_configure(edev))
{
_ecore_drm_evdev_device_destroy(edev);
return NULL;
}
edev->event_process = _device_process;
edev->hdlr =
ecore_main_fd_handler_add(edev->fd, ECORE_FD_READ,
_cb_device_data, edev, NULL, NULL);
if (!edev->hdlr)
{
ERR("Could not create fd handler");
_ecore_drm_evdev_device_destroy(edev);
return NULL;
}
return edev;
}
void
_ecore_drm_evdev_device_destroy(Ecore_Drm_Evdev *dev)
{
if (!dev) return;
if (dev->path) eina_stringshare_del(dev->path);
if (dev->name) eina_stringshare_del(dev->name);
if (dev->hdlr) ecore_main_fd_handler_del(dev->hdlr);
free(dev);
}

View File

@ -0,0 +1,151 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "ecore_drm_private.h"
/**
* @defgroup Ecore_Drm_Fb_Group
*
*/
/* TODO: DOXY !! */
EAPI Ecore_Drm_Fb *
ecore_drm_fb_create(Ecore_Drm_Device *dev, int width, int height)
{
Ecore_Drm_Fb *fb;
struct drm_mode_create_dumb carg;
struct drm_mode_destroy_dumb darg;
struct drm_mode_map_dumb marg;
if (!(fb = calloc(1, sizeof(Ecore_Drm_Fb)))) return NULL;
memset(&carg, 0, sizeof(struct drm_mode_create_dumb));
carg.bpp = 32; // FIXME: Hard-coded depth
carg.width = width;
carg.height = height;
if (drmIoctl(dev->drm.fd, DRM_IOCTL_MODE_CREATE_DUMB, &carg))
{
ERR("Could not create dumb framebuffer: %m");
goto create_err;
}
fb->from_client = EINA_TRUE;
fb->hdl = carg.handle;
fb->stride = carg.pitch;
fb->size = carg.size;
fb->fd = dev->drm.fd;
if (drmModeAddFB(dev->drm.fd, width, height, 24, 32,
fb->stride, fb->hdl, &fb->id))
{
ERR("Could not add framebuffer: %m");
goto add_err;
}
memset(&marg, 0, sizeof(struct drm_mode_map_dumb));
marg.handle = fb->hdl;
if (drmIoctl(dev->drm.fd, DRM_IOCTL_MODE_MAP_DUMB, &marg))
{
ERR("Could not map framebuffer: %m");
goto map_err;
}
fb->mmap =
mmap(0, fb->size, PROT_WRITE | PROT_READ, MAP_SHARED,
dev->drm.fd, marg.offset);
if (fb->mmap == MAP_FAILED)
{
ERR("Could not mmap framebuffer space: %m");
goto map_err;
}
memset(fb->mmap, 0, fb->size);
return fb;
map_err:
drmModeRmFB(fb->fd, fb->id);
add_err:
memset(&darg, 0, sizeof(struct drm_mode_destroy_dumb));
darg.handle = fb->hdl;
drmIoctl(dev->drm.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &darg);
create_err:
free(fb);
return NULL;
}
EAPI void
ecore_drm_fb_destroy(Ecore_Drm_Fb *fb)
{
struct drm_mode_destroy_dumb darg;
if ((!fb) || (!fb->mmap)) return;
if (fb->id) drmModeRmFB(fb->fd, fb->id);
munmap(fb->mmap, fb->size);
memset(&darg, 0, sizeof(struct drm_mode_destroy_dumb));
darg.handle = fb->hdl;
drmIoctl(fb->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &darg);
free(fb);
}
/* #ifdef HAVE_GBM */
/* static void */
/* _ecore_drm_fb_user_data_destroy(struct gbm_bo *bo EINA_UNUSED, void *data) */
/* { */
/* Ecore_Drm_Fb *fb; */
/* if (!(fb = data)) return; */
/* ecore_drm_fb_destroy(fb); */
/* } */
/* Ecore_Drm_Fb * */
/* _ecore_drm_fb_bo_get(Ecore_Drm_Device *dev, struct gbm_bo *bo) */
/* { */
/* Ecore_Drm_Fb *fb; */
/* unsigned int width, height; */
/* unsigned int h[4], p[4], o[4]; */
/* int ret = -1; */
/* if ((fb = gbm_bo_get_user_data(bo))) return fb; */
/* if (!(fb = calloc(1, sizeof(Ecore_Drm_Fb)))) return NULL; */
/* fb->bo = bo; */
/* width = gbm_bo_get_width(bo); */
/* height = gbm_bo_get_height(bo); */
/* fb->stride = gbm_bo_get_stride(bo); */
/* fb->hdl = gbm_bo_get_handle(bo).u32; */
/* fb->size = (fb->stride * height); */
/* fb->fd = dev->drm.fd; */
/* h[0] = fb->hdl; */
/* p[0] = fb->stride; */
/* o[0] = 0; */
/* ret = drmModeAddFB2(dev->drm.fd, width, height, dev->format, h, p, o, */
/* &fb->id, 0); */
/* if (ret) */
/* { */
/* ret = drmModeAddFB(dev->drm.fd, width, height, 24, 32, */
/* fb->stride, fb->hdl, &fb->id); */
/* } */
/* if (ret) */
/* { */
/* ERR("Error during ModeAddFb"); */
/* free(fb); */
/* return NULL; */
/* } */
/* gbm_bo_set_user_data(bo, fb, _ecore_drm_fb_user_data_destroy); */
/* return fb; */
/* } */
/* #endif */

View File

@ -0,0 +1,303 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "ecore_drm_private.h"
/* local functions */
static Ecore_Drm_Seat *
_seat_get(Ecore_Drm_Input *input, const char *seat)
{
Ecore_Drm_Seat *s;
Eina_List *l;
EINA_LIST_FOREACH(input->dev->seats, l, s)
{
if (!strcmp(s->name, seat)) return s;
}
if (!(s = calloc(1, sizeof(Ecore_Drm_Seat))))
return NULL;
s->input = input;
s->name = eina_stringshare_add(seat);
input->dev->seats = eina_list_append(input->dev->seats, s);
return s;
}
static Eina_Bool
_device_add(Ecore_Drm_Input *input, struct udev_device *device)
{
Ecore_Drm_Evdev *edev;
Ecore_Drm_Seat *seat;
const char *dev_seat, *wl_seat;
const char *node;
char n[PATH_MAX];
int fd = -1;
if (!(dev_seat = udev_device_get_property_value(device, "ID_SEAT")))
dev_seat = "seat0";
if (strcmp(dev_seat, input->seat)) return EINA_FALSE;
if (!(wl_seat = udev_device_get_property_value(device, "WL_SEAT")))
wl_seat = "seat0";
if (!(seat = _seat_get(input, wl_seat)))
return EINA_FALSE;
node = udev_device_get_devnode(device);
strcpy(n, node);
fd = open(n, O_RDWR | O_NONBLOCK);
/* _ecore_drm_message_send(ECORE_DRM_OP_DEVICE_OPEN, -1, n, strlen(n)); */
/* _ecore_drm_message_receive(ECORE_DRM_OP_DEVICE_OPEN, &fd, NULL, 0); */
/* DBG("Opened Restricted Input: %s %d", node, fd); */
if (!(edev = _ecore_drm_evdev_device_create(seat, node, fd)))
{
close(fd);
/* _ecore_drm_message_send(ECORE_DRM_OP_DEVICE_CLOSE, fd, NULL, 0); */
/* _ecore_drm_message_receive(ECORE_DRM_OP_DEVICE_OPEN, &fd, NULL, 0); */
return EINA_FALSE;
}
seat->devices = eina_list_append(seat->devices, edev);
/* TODO: finish */
return EINA_TRUE;
}
static void
_device_remove(Ecore_Drm_Input *input, const char *device)
{
Ecore_Drm_Seat *seat;
Eina_List *l;
if (!input) return;
EINA_LIST_FOREACH(input->dev->seats, l, seat)
{
Ecore_Drm_Evdev *edev;
Eina_List *ll;
EINA_LIST_FOREACH(seat->devices, ll, edev)
{
if (!strcmp(edev->path, device))
{
seat->devices = eina_list_remove(seat->devices, edev);
_ecore_drm_evdev_device_destroy(edev);
break;
}
}
}
}
static Eina_Bool
_cb_input_event(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED)
{
Ecore_Drm_Input *input;
struct udev_device *udevice;
const char *act;
DBG("Input Event");
if (!(input = data)) return EINA_FALSE;
if (!(udevice = udev_monitor_receive_device(input->monitor)))
return EINA_TRUE;
if (!(act = udev_device_get_action(udevice))) return EINA_TRUE;
if (strncmp("event", udev_device_get_sysname(udevice), 5) != 0)
goto err;
if (!strcmp(act, "add"))
{
DBG("\tDevice Added");
_device_add(input, udevice);
}
else if (!strcmp(act, "remove"))
{
const char *node;
node = udev_device_get_devnode(udevice);
DBG("\tDevice Removed: %s", node);
_device_remove(input, node);
}
return EINA_TRUE;
err:
if (udevice) udev_device_unref(udevice);
return EINA_TRUE;
}
static Eina_Bool
_devices_add(Ecore_Drm_Input *input)
{
struct udev_enumerate *uenum;
struct udev_list_entry *uentry;
struct udev_device *udevice;
const char *path, *name;
Eina_Bool found = EINA_FALSE;
uenum = udev_enumerate_new(udev);
udev_enumerate_add_match_subsystem(uenum, "input");
udev_enumerate_scan_devices(uenum);
udev_list_entry_foreach(uentry, udev_enumerate_get_list_entry(uenum))
{
path = udev_list_entry_get_name(uentry);
udevice = udev_device_new_from_syspath(udev, path);
name = udev_device_get_sysname(udevice);
if (strncmp("event", name, 5) != 0)
{
udev_device_unref(udevice);
continue;
}
if (!_device_add(input, udevice))
{
udev_device_unref(udevice);
continue;
}
found = EINA_TRUE;
udev_device_unref(udevice);
}
udev_enumerate_unref(uenum);
if (!found)
{
ERR("No Input Devices Found");
return EINA_FALSE;
}
return EINA_TRUE;
}
/* public functions */
EAPI Eina_Bool
ecore_drm_inputs_create(Ecore_Drm_Device *dev)
{
Ecore_Drm_Input *input;
/* check for valid device */
if ((!dev) || (!udev)) return EINA_FALSE;
/* try to allocate space for input structure */
if (!(input = calloc(1, sizeof(Ecore_Drm_Input))))
return EINA_FALSE;
/* FIXME: Hardcoded seat name */
input->seat = eina_stringshare_add("seat0");
input->dev = dev;
/* try to enable this input */
if (!ecore_drm_inputs_enable(input))
{
ERR("Could not enable input");
if (input->seat) eina_stringshare_del(input->seat);
free(input);
return EINA_FALSE;
}
/* add this input to dev */
dev->inputs = eina_list_append(dev->inputs, input);
return EINA_TRUE;
}
EAPI void
ecore_drm_inputs_destroy(Ecore_Drm_Device *dev)
{
Ecore_Drm_Seat *seat;
Eina_List *l;
if (!dev) return;
EINA_LIST_FOREACH(dev->seats, l, seat)
{
Ecore_Drm_Evdev *edev;
EINA_LIST_FREE(seat->devices, edev)
_ecore_drm_evdev_device_destroy(edev);
}
}
EAPI Eina_Bool
ecore_drm_inputs_enable(Ecore_Drm_Input *input)
{
/* check for valid input */
if (!input) return EINA_FALSE;
if (!input->monitor)
input->monitor = udev_monitor_new_from_netlink(udev, "udev");
if (!input->monitor)
{
ERR("Could not create udev monitor: %m");
return EINA_FALSE;
}
/* setup input filter */
udev_monitor_filter_add_match_subsystem_devtype(input->monitor,
"input", NULL);
/* try to enable receiving udev events */
if (udev_monitor_enable_receiving(input->monitor))
{
ERR("Could not bind udev monitor: %m");
udev_monitor_unref(input->monitor);
return EINA_FALSE;
}
/* save the fd */
if ((input->fd = udev_monitor_get_fd(input->monitor)) < 0)
{
ERR("Input monitor has no fd: %m");
udev_monitor_unref(input->monitor);
return EINA_FALSE;
}
/* create fd handler */
if (!input->hdlr)
{
input->hdlr =
ecore_main_fd_handler_add(input->fd, ECORE_FD_READ,
_cb_input_event, input, NULL, NULL);
}
if (!input->hdlr)
{
ERR("Failed to setup input fd handler: %m");
udev_monitor_unref(input->monitor);
return EINA_FALSE;
}
/* try to add devices */
if (!_devices_add(input))
{
ERR("Could not add input devices");
udev_monitor_unref(input->monitor);
return EINA_FALSE;
}
input->enabled = EINA_TRUE;
input->suspended = EINA_FALSE;
return EINA_TRUE;
}
EAPI void
ecore_drm_inputs_disable(Ecore_Drm_Input *input)
{
if (!input) return;
}

View File

@ -0,0 +1,703 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "ecore_drm_private.h"
#define ALEN(array) (sizeof(array) / sizeof(array)[0])
static const char *conn_types[] =
{
"None", "VGA", "DVI", "DVI", "DVI",
"Composite", "TV", "LVDS", "CTV", "DIN",
"DP", "HDMI", "HDMI", "TV", "eDP",
};
/* local functions */
/* #ifdef HAVE_GBM */
/* static Eina_Bool */
/* _ecore_drm_output_context_create(Ecore_Drm_Device *dev, EGLSurface surface) */
/* { */
/* EGLBoolean r; */
/* static const EGLint attribs[] = */
/* { */
/* EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE */
/* }; */
/* if ((!dev->egl.disp) || (!dev->egl.cfg)) return EINA_FALSE; */
/* if (!eglBindAPI(EGL_OPENGL_ES_API)) */
/* { */
/* ERR("Could not bind egl api"); */
/* return EINA_FALSE; */
/* } */
/* dev->egl.ctxt = */
/* eglCreateContext(dev->egl.disp, dev->egl.cfg, EGL_NO_CONTEXT, attribs); */
/* if (!dev->egl.ctxt) */
/* { */
/* ERR("Could not create Egl Context"); */
/* return EINA_FALSE; */
/* } */
/* r = eglMakeCurrent(dev->egl.disp, surface, surface, dev->egl.ctxt); */
/* if (r == EGL_FALSE) */
/* { */
/* ERR("Could not make surface current"); */
/* return EINA_FALSE; */
/* } */
/* return EINA_TRUE; */
/* } */
/* static Eina_Bool */
/* _ecore_drm_output_hardware_setup(Ecore_Drm_Device *dev, Ecore_Drm_Output *output) */
/* { */
/* unsigned int i = 0; */
/* int flags = 0; */
/* int w = 0, h = 0; */
/* if ((!dev) || (!output)) return EINA_FALSE; */
/* if (output->current_mode) */
/* { */
/* w = output->current_mode->width; */
/* h = output->current_mode->height; */
/* } */
/* else */
/* { */
/* w = 1024; */
/* h = 768; */
/* } */
/* flags = (GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); */
/* if (!(output->surface = */
/* gbm_surface_create(dev->gbm, w, h, GBM_FORMAT_ARGB8888, flags))) */
/* { */
/* ERR("Could not create output surface"); */
/* return EINA_FALSE; */
/* } */
/* if (!(output->egl.surface = */
/* eglCreateWindowSurface(dev->egl.disp, dev->egl.cfg, */
/* output->surface, NULL))) */
/* { */
/* ERR("Could not create output egl surface"); */
/* gbm_surface_destroy(output->surface); */
/* return EINA_FALSE; */
/* } */
/* if (!dev->egl.ctxt) */
/* { */
/* if (!_ecore_drm_output_context_create(dev, output->egl.surface)) */
/* { */
/* ERR("Could not create context"); */
/* gbm_surface_destroy(output->surface); */
/* return EINA_FALSE; */
/* } */
/* } */
/* flags = (GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE); */
/* for (i = 0; i < NUM_FRAME_BUFFERS; i++) */
/* { */
/* if (output->cursor[i]) continue; */
/* if (!(output->cursor[i] = */
/* gbm_bo_create(dev->gbm, 64, 64, dev->format, flags))) */
/* { */
/* continue; */
/* } */
/* } */
/* if ((!output->cursor[0]) || (!output->cursor[1])) */
/* { */
/* WRN("Hardware Cursor Buffers not available"); */
/* dev->cursors_broken = EINA_TRUE; */
/* } */
/* return EINA_TRUE; */
/* } */
/* static void */
/* _ecore_drm_output_hardware_render(Ecore_Drm_Output *output) */
/* { */
/* struct gbm_bo *bo; */
/* int ret; */
/* if (!output) return; */
/* if (!output->current_mode) return; */
/* glViewport(output->x, output->y, */
/* output->current_mode->width, output->current_mode->height); */
/* if (eglMakeCurrent(output->dev->egl.disp, output->egl.surface, */
/* output->egl.surface, output->dev->egl.ctxt) == EGL_FALSE) */
/* return; */
/* glClearColor(1.0, 1.0, 0.0, 1.0); */
/* glClear(GL_COLOR_BUFFER_BIT); */
/* glFlush(); */
/* eglSwapBuffers(output->dev->egl.disp, output->egl.surface); */
/* if (!(bo = gbm_surface_lock_front_buffer(output->surface))) */
/* { */
/* ERR("Failed to lock front buffer"); */
/* return; */
/* } */
/* if (!(output->next = _ecore_drm_fb_bo_get(output->dev, bo))) */
/* { */
/* ERR("Failed to get FB from bo"); */
/* gbm_surface_release_buffer(output->surface, bo); */
/* } */
/* } */
/* #endif */
static Eina_Bool
_ecore_drm_output_software_setup(Ecore_Drm_Device *dev, Ecore_Drm_Output *output)
{
unsigned int i = 0;
int w = 0, h = 0;
if ((!dev) || (!output)) return EINA_FALSE;
if (output->current_mode)
{
w = output->current_mode->width;
h = output->current_mode->height;
}
else
{
w = 1024;
h = 768;
}
for (i = 0; i < NUM_FRAME_BUFFERS; i++)
{
if (!(output->dumb[i] = ecore_drm_fb_create(dev, w, h)))
{
ERR("Could not create dumb framebuffer %d", i);
goto err;
}
}
return EINA_TRUE;
err:
for (i = 0; i < NUM_FRAME_BUFFERS; i++)
{
if (output->dumb[i]) ecore_drm_fb_destroy(output->dumb[i]);
output->dumb[i] = NULL;
}
return EINA_FALSE;
}
static void
_ecore_drm_output_software_render(Ecore_Drm_Output *output)
{
if (!output) return;
if (!output->current_mode) return;
}
static int
_ecore_drm_output_crtc_find(Ecore_Drm_Device *dev, drmModeRes *res, drmModeConnector *conn)
{
drmModeEncoder *enc;
unsigned int p;
int i = 0, j = 0;
for (j = 0; j < conn->count_encoders; j++)
{
/* get the encoder on this connector */
if (!(enc = drmModeGetEncoder(dev->drm.fd, conn->encoders[j])))
{
ERR("Failed to get encoder: %m");
return -1;
}
p = enc->possible_crtcs;
drmModeFreeEncoder(enc);
for (i = 0; i < res->count_crtcs; i++)
{
if ((p & (1 << i)) &&
(!(dev->crtc_allocator & (1 << res->crtcs[i]))))
{
return i;
}
}
}
return -1;
}
static Ecore_Drm_Output_Mode *
_ecore_drm_output_mode_add(Ecore_Drm_Output *output, drmModeModeInfo *info)
{
Ecore_Drm_Output_Mode *mode;
uint64_t refresh;
/* try to allocate space for mode */
if (!(mode = malloc(sizeof(Ecore_Drm_Output_Mode))))
{
ERR("Could not allocate space for mode");
return NULL;
}
mode->flags = 0;
mode->width = info->hdisplay;
mode->height = info->vdisplay;
refresh = (info->clock * 1000000LL / info->htotal + info->vtotal / 2) / info->vtotal;
if (info->flags & DRM_MODE_FLAG_INTERLACE)
refresh *= 2;
if (info->flags & DRM_MODE_FLAG_DBLSCAN)
refresh /= 2;
if (info->vscan > 1)
refresh /= info->vscan;
mode->refresh = refresh;
mode->info = *info;
/* DBG("Added Mode: %dx%d@%d to Output %d", */
/* mode->width, mode->height, mode->refresh, output->crtc_id); */
output->modes = eina_list_append(output->modes, mode);
return mode;
}
static Ecore_Drm_Output *
_ecore_drm_output_create(Ecore_Drm_Device *dev, drmModeRes *res, drmModeConnector *conn, int x, int y)
{
Ecore_Drm_Output *output;
Ecore_Drm_Output_Mode *mode;
const char *conn_name;
char name[32];
int i = 0;
drmModeEncoder *enc;
drmModeModeInfo crtc_mode;
drmModeCrtc *crtc;
Eina_List *l;
i = _ecore_drm_output_crtc_find(dev, res, conn);
if (i < 0)
{
ERR("Could not find crtc or encoder for connector");
return NULL;
}
/* try to allocate space for output */
if (!(output = calloc(1, sizeof(Ecore_Drm_Output))))
{
ERR("Could not allocate space for output");
return NULL;
}
output->dev = dev;
output->x = x;
output->y = y;
output->subpixel = conn->subpixel;
output->make = eina_stringshare_add("unknown");
output->model = eina_stringshare_add("unknown");
if (conn->connector_type < ALEN(conn_types))
conn_name = conn_types[conn->connector_type];
else
conn_name = "UNKNOWN";
snprintf(name, sizeof(name), "%s%d", conn_name, conn->connector_type_id);
output->name = eina_stringshare_add(name);
output->crtc_id = res->crtcs[i];
dev->crtc_allocator |= (1 << output->crtc_id);
output->conn_id = conn->connector_id;
output->crtc = drmModeGetCrtc(dev->drm.fd, output->crtc_id);
memset(&mode, 0, sizeof(mode));
if ((enc = drmModeGetEncoder(dev->drm.fd, conn->encoder_id)))
{
crtc = drmModeGetCrtc(dev->drm.fd, enc->crtc_id);
drmModeFreeEncoder(enc);
if (!crtc) goto mode_err;
if (crtc->mode_valid) crtc_mode = crtc->mode;
drmModeFreeCrtc(crtc);
}
for (i = 0; i < conn->count_modes; i++)
{
if (!(mode = _ecore_drm_output_mode_add(output, &conn->modes[i])))
{
ERR("Failed to add mode to output");
goto mode_err;
}
}
EINA_LIST_REVERSE_FOREACH(output->modes, l, mode)
{
if (!memcmp(&crtc_mode, &mode->info, sizeof(crtc_mode)))
{
output->current_mode = mode;
break;
}
}
if (!output->current_mode)
output->current_mode = _ecore_drm_output_mode_add(output, &crtc_mode);
/* #ifdef HAVE_GBM */
/* if ((dev->use_hw_accel) && (dev->gbm)) */
/* { */
/* if (!_ecore_drm_output_hardware_setup(dev, output)) */
/* { */
/* ERR("Could not setup output for hardware acceleration"); */
/* dev->use_hw_accel = EINA_FALSE; */
/* if (!_ecore_drm_output_software_setup(dev, output)) */
/* goto mode_err; */
/* else */
/* DBG("Setup Output %d for Software Rendering", output->crtc_id); */
/* } */
/* else */
/* DBG("Setup Output %d for Hardware Acceleration", output->crtc_id); */
/* } */
/* else */
/* #endif */
{
dev->use_hw_accel = EINA_FALSE;
if (!_ecore_drm_output_software_setup(dev, output))
goto mode_err;
else
DBG("Setup Output %d for Software Rendering", output->crtc_id);
}
/* TODO */
return output;
mode_err:
EINA_LIST_FREE(output->modes, mode)
free(mode);
drmModeFreeCrtc(output->crtc);
dev->crtc_allocator &= ~(1 << output->crtc_id);
free(output);
return NULL;
}
void
_ecore_drm_output_frame_finish(Ecore_Drm_Output *output)
{
if (!output) return;
if (output->need_repaint) ecore_drm_output_repaint(output);
output->repaint_scheduled = EINA_FALSE;
}
void
_ecore_drm_output_fb_release(Ecore_Drm_Output *output, Ecore_Drm_Fb *fb)
{
if ((!output) || (!fb)) return;
if ((fb->mmap) && (fb != output->dumb[0]) && (fb != output->dumb[1]))
ecore_drm_fb_destroy(fb);
/* #ifdef HAVE_GBM */
/* else if (fb->bo) */
/* gbm_bo_destroy(fb->bo); */
/* #endif */
}
void
_ecore_drm_output_repaint_start(Ecore_Drm_Output *output)
{
unsigned int fb;
DBG("Output Repaint Start");
if (!output) return;
if (!output->current)
{
DBG("\tNo Current FB");
goto finish;
}
fb = output->current->id;
if (drmModePageFlip(output->dev->drm.fd, output->crtc_id, fb,
DRM_MODE_PAGE_FLIP_EVENT, output) < 0)
{
ERR("Could not schedule output page flip event");
goto finish;
}
return;
finish:
_ecore_drm_output_frame_finish(output);
}
/**
* @defgroup Ecore_Drm_Output_Group
*
*/
/* TODO: DOXY !! */
/* public functions */
EAPI Eina_Bool
ecore_drm_outputs_create(Ecore_Drm_Device *dev)
{
Eina_Bool ret = EINA_TRUE;
Ecore_Drm_Output *output;
drmModeConnector *conn;
drmModeRes *res;
drmModeCrtc *crtc;
int i = 0, x = 0, y = 0;
/* DBG("Create outputs for %d", dev->drm.fd); */
/* get the resources */
if (!(res = drmModeGetResources(dev->drm.fd)))
{
ERR("Could not get resources for drm card: %m");
return EINA_FALSE;
}
if (!(dev->crtcs = calloc(res->count_crtcs, sizeof(unsigned int))))
{
ERR("Could not allocate space for crtcs");
/* free resources */
drmModeFreeResources(res);
return EINA_FALSE;
}
dev->crtc_count = res->count_crtcs;
memcpy(dev->crtcs, res->crtcs, sizeof(unsigned int) * res->count_crtcs);
dev->min_width = res->min_width;
dev->min_height = res->min_height;
dev->max_width = res->max_width;
dev->max_height = res->max_height;
for (i = 0; i < res->count_connectors; i++)
{
if (i > 0) break;
/* get the connector */
if (!(conn = drmModeGetConnector(dev->drm.fd, res->connectors[i])))
continue;
if ((conn->connection == DRM_MODE_CONNECTED) &&
(conn->count_modes > 0))
{
drmModeEncoder *enc;
/* create output for this connector */
if (!(output = _ecore_drm_output_create(dev, res, conn, x, y)))
{
/* free the connector */
drmModeFreeConnector(conn);
continue;
}
output->drm_fd = dev->drm.fd;
dev->outputs = eina_list_append(dev->outputs, output);
if (!(enc = drmModeGetEncoder(dev->drm.fd, conn->encoder_id)))
{
drmModeFreeConnector(conn);
continue;
}
if (!(crtc = drmModeGetCrtc(dev->drm.fd, enc->crtc_id)))
{
drmModeFreeEncoder(enc);
drmModeFreeConnector(conn);
continue;
}
x += crtc->width;
drmModeFreeCrtc(crtc);
drmModeFreeEncoder(enc);
}
/* free the connector */
drmModeFreeConnector(conn);
}
ret = EINA_TRUE;
if (eina_list_count(dev->outputs) < 1)
{
ret = EINA_FALSE;
free(dev->crtcs);
}
/* free resources */
drmModeFreeResources(res);
/* TODO: add hook for udev drm output updates */
return ret;
}
EAPI void
ecore_drm_output_free(Ecore_Drm_Output *output)
{
Ecore_Drm_Output_Mode *mode;
/* check for valid output */
if (!output) return;
/* free modes */
EINA_LIST_FREE(output->modes, mode)
free(mode);
/* free strings */
if (output->name) eina_stringshare_del(output->name);
if (output->model) eina_stringshare_del(output->model);
if (output->make) eina_stringshare_del(output->make);
free(output);
}
EAPI void
ecore_drm_output_cursor_size_set(Ecore_Drm_Output *output, int handle, int w, int h)
{
if (!output) return;
drmModeSetCursor(output->drm_fd, output->crtc_id, handle, w, h);
}
EAPI Eina_Bool
ecore_drm_output_enable(Ecore_Drm_Output *output)
{
Ecore_Drm_Output_Mode *mode;
if ((!output) || (!output->current)) return EINA_FALSE;
mode = output->current_mode;
if (drmModeSetCrtc(output->drm_fd, output->crtc_id, output->current->id,
0, 0, &output->conn_id, 1, &mode->info) < 0)
{
ERR("Could not set output crtc: %m");
return EINA_FALSE;
}
return EINA_TRUE;
}
EAPI void
ecore_drm_output_fb_release(Ecore_Drm_Output *output, Ecore_Drm_Fb *fb)
{
if ((!output) || (!fb)) return;
if ((fb->mmap) && (fb != output->dumb[0]) && (fb != output->dumb[1]))
ecore_drm_fb_destroy(fb);
/* #ifdef HAVE_GBM */
/* else if (fb->bo) */
/* { */
/* if (fb->from_client) */
/* gbm_bo_destroy(fb->bo); */
/* else */
/* gbm_surface_release_buffer(output->surface, fb->bo); */
/* } */
/* #endif */
}
EAPI void
ecore_drm_output_repaint(Ecore_Drm_Output *output)
{
Eina_List *l;
Ecore_Drm_Sprite *sprite;
int ret = 0;
if (!output) return;
DBG("Output Repaint: %d %d", output->crtc_id, output->conn_id);
/* TODO: assign planes ? */
if (!output->next)
{
/* #ifdef HAVE_GBM */
/* if (output->dev->use_hw_accel) */
/* { */
/* _ecore_drm_output_hardware_render(output); */
/* } */
/* else */
/* #endif */
{
_ecore_drm_output_software_render(output);
}
}
if (!output->next)
{
DBG("\tNo Next Fb");
return;
}
output->need_repaint = EINA_FALSE;
if (!output->current)
{
Ecore_Drm_Output_Mode *mode;
mode = output->current_mode;
ret = drmModeSetCrtc(output->dev->drm.fd, output->crtc_id,
output->next->id, 0, 0, &output->conn_id, 1,
&mode->info);
if (ret)
{
ERR("Setting output mode failed");
goto err;
}
}
if (drmModePageFlip(output->dev->drm.fd, output->crtc_id, output->next->id,
DRM_MODE_PAGE_FLIP_EVENT, output) < 0)
{
ERR("Scheduling pageflip failed");
goto err;
}
output->pending_flip = EINA_TRUE;
EINA_LIST_FOREACH(output->dev->sprites, l, sprite)
{
unsigned int flags = 0, id = 0;
drmVBlank vbl =
{
.request.type = (DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT),
.request.sequence = 1,
};
if (((!sprite->current_fb) && (!sprite->next_fb)) ||
(!ecore_drm_sprites_crtc_supported(output, sprite->crtcs)))
continue;
if ((sprite->next_fb) && (!output->dev->cursors_broken))
id = sprite->next_fb->id;
ecore_drm_sprites_fb_set(sprite, id, flags);
vbl.request.signal = (unsigned long)sprite;
ret = drmWaitVBlank(output->dev->drm.fd, &vbl);
if (ret) ERR("Error Wait VBlank: %m");
sprite->output = output;
output->pending_vblank = EINA_TRUE;
}
return;
err:
if (output->next)
{
ecore_drm_output_fb_release(output, output->next);
output->next = NULL;
}
}

View File

@ -0,0 +1,281 @@
#ifndef _ECORE_DRM_PRIVATE_H
# define _ECORE_DRM_PRIVATE_H
# include "Ecore.h"
# include "ecore_private.h"
# include "Ecore_Input.h"
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# include <errno.h>
# include <sys/mman.h>
# include <fcntl.h>
# include <libudev.h>
# include <linux/input.h>
//# include <libinput.h>
# include <xf86drm.h>
# include <xf86drmMode.h>
# include <drm_fourcc.h>
/* # ifdef HAVE_GBM */
/* # include <gbm.h> */
/* # include <EGL/egl.h> */
/* # include <EGL/eglext.h> */
/* # include <GLES2/gl2.h> */
/* # include <GLES2/gl2ext.h> */
/* # endif */
# include <Ecore_Drm.h>
# ifndef DRM_MAJOR
# define DRM_MAJOR 226
# endif
# ifndef DRM_CAP_TIMESTAMP_MONOTONIC
# define DRM_CAP_TIMESTAMP_MONOTONIC 0x6
# endif
# ifdef ECORE_DRM_DEFAULT_LOG_COLOR
# undef ECORE_DRM_DEFAULT_LOG_COLOR
# endif
# define ECORE_DRM_DEFAULT_LOG_COLOR EINA_COLOR_BLUE
# define EVDEV_SEAT_POINTER (1 << 0)
# define EVDEV_SEAT_KEYBOARD (1 << 1)
# define EVDEV_SEAT_TOUCH (1 << 2)
# ifdef ERR
# undef ERR
# endif
# ifdef DBG
# undef DBG
# endif
# ifdef INF
# undef INF
# endif
# ifdef WRN
# undef WRN
# endif
# ifdef CRIT
# undef CRIT
# endif
extern int _ecore_drm_log_dom;
/* undef this for non-testing builds */
# define LOG_TO_FILE
# ifdef LOG_TO_FILE
extern FILE *lg;
# define ERR(...) \
EINA_LOG_DOM_ERR(_ecore_drm_log_dom, __VA_ARGS__); \
fflush(lg);
# define DBG(...) \
EINA_LOG_DOM_DBG(_ecore_drm_log_dom, __VA_ARGS__); \
fflush(lg);
# define INF(...) \
EINA_LOG_DOM_INFO(_ecore_drm_log_dom, __VA_ARGS__); \
fflush(lg);
# define WRN(...) \
EINA_LOG_DOM_WARN(_ecore_drm_log_dom, __VA_ARGS__); \
fflush(lg);
# define CRIT(...) \
EINA_LOG_DOM_CRIT(_ecore_drm_log_dom, __VA_ARGS__); \
fflush(lg);
# else
# define ERR(...) EINA_LOG_DOM_ERR(_ecore_drm_log_dom, __VA_ARGS__)
# define DBG(...) EINA_LOG_DOM_DBG(_ecore_drm_log_dom, __VA_ARGS__)
# define INF(...) EINA_LOG_DOM_INFO(_ecore_drm_log_dom, __VA_ARGS__)
# define WRN(...) EINA_LOG_DOM_WARN(_ecore_drm_log_dom, __VA_ARGS__)
# define CRIT(...) EINA_LOG_DOM_CRIT(_ecore_drm_log_dom, __VA_ARGS__)
# endif
extern struct udev *udev;
struct _Ecore_Drm_Output_Mode
{
unsigned int flags;
int width, height;
unsigned int refresh;
drmModeModeInfo info;
};
struct _Ecore_Drm_Output
{
Ecore_Drm_Device *dev;
unsigned int crtc_id;
unsigned int conn_id;
drmModeCrtcPtr crtc;
int x, y;
int drm_fd;
Eina_Bool need_repaint : 1;
Eina_Bool repaint_scheduled : 1;
Eina_Bool pending_flip : 1;
Eina_Bool pending_vblank : 1;
const char *make, *model, *name;
unsigned int subpixel;
Ecore_Drm_Output_Mode *current_mode;
Eina_List *modes;
Ecore_Drm_Fb *current, *next;
Ecore_Drm_Fb *dumb[NUM_FRAME_BUFFERS];
/* # ifdef HAVE_GBM */
/* struct gbm_surface *surface; */
/* struct gbm_bo *cursor[NUM_FRAME_BUFFERS]; */
/* struct */
/* { */
/* EGLSurface surface; */
/* } egl; */
/* # endif */
/* TODO: finish */
};
struct _Ecore_Drm_Seat
{
// struct libinput_seat *seat;
const char *name;
Ecore_Drm_Input *input;
Eina_List *devices;
};
struct _Ecore_Drm_Input
{
int fd;
const char *seat;
struct udev_monitor *monitor;
Ecore_Fd_Handler *hdlr;
Ecore_Drm_Device *dev;
Eina_Bool enabled : 1;
Eina_Bool suspended : 1;
};
struct _Ecore_Drm_Evdev
{
Ecore_Drm_Seat *seat;
/* struct libinput *linput; */
/* struct libinput_device *dev; */
const char *name, *path;
int fd;
struct
{
int min_x, min_y;
int max_x, max_y;
int x, y;
} abs;
Ecore_Drm_Evdev_Event_Type pending_event;
Ecore_Drm_Evdev_Capabilities caps;
Ecore_Drm_Seat_Capabilities seat_caps;
void (*event_process)(Ecore_Drm_Evdev *dev, struct input_event *event, int count);
Ecore_Fd_Handler *hdlr;
};
struct _Ecore_Drm_Sprite
{
Ecore_Drm_Fb *current_fb, *next_fb;
Ecore_Drm_Output *output;
int drm_fd;
unsigned int crtcs;
unsigned int plane_id;
struct
{
int x, y;
unsigned int w, h;
} src, dest;
unsigned int num_formats;
unsigned int formats[];
};
struct _Ecore_Drm_Device
{
int id;
const char *seat;
struct
{
int fd;
const char *name;
const char *path;
clockid_t clock;
Ecore_Fd_Handler *hdlr;
Ecore_Idle_Enterer *idler;
} drm;
unsigned int min_width, min_height;
unsigned int max_width, max_height;
unsigned int crtc_count;
unsigned int *crtcs;
unsigned int crtc_allocator;
Eina_List *seats;
Eina_List *inputs;
Eina_List *outputs;
Eina_List *sprites;
struct
{
int fd;
const char *name;
Ecore_Event_Handler *event_hdlr;
} tty;
unsigned int format;
Eina_Bool use_hw_accel : 1;
Eina_Bool cursors_broken : 1;
/* #ifdef HAVE_GBM */
/* struct gbm_device *gbm; */
/* struct */
/* { */
/* EGLDisplay disp; */
/* EGLContext ctxt; */
/* EGLConfig cfg; */
/* } egl; */
/* #endif */
};
void _ecore_drm_message_send(int opcode, int fd, void *data, size_t bytes);
Eina_Bool _ecore_drm_message_receive(int opcode, int *fd, void **data, size_t bytes);
Ecore_Drm_Evdev *_ecore_drm_evdev_device_create(Ecore_Drm_Seat *seat, const char *path, int fd);
void _ecore_drm_evdev_device_destroy(Ecore_Drm_Evdev *evdev);
/* int _ecore_drm_evdev_event_process(struct libinput_event *event); */
Ecore_Drm_Fb *_ecore_drm_fb_create(Ecore_Drm_Device *dev, int width, int height);
void _ecore_drm_fb_destroy(Ecore_Drm_Fb *fb);
/* #ifdef HAVE_GBM */
/* Ecore_Drm_Fb *_ecore_drm_fb_bo_get(Ecore_Drm_Device *dev, struct gbm_bo *bo); */
/* #endif */
void _ecore_drm_output_fb_release(Ecore_Drm_Output *output, Ecore_Drm_Fb *fb);
void _ecore_drm_output_repaint_start(Ecore_Drm_Output *output);
void _ecore_drm_output_frame_finish(Ecore_Drm_Output *output);
#endif

View File

@ -0,0 +1,115 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "ecore_drm_private.h"
/**
* @defgroup Ecore_Drm_Fb_Group
*
*/
/* TODO: DOXY !! */
EAPI Eina_Bool
ecore_drm_sprites_create(Ecore_Drm_Device *dev)
{
drmModePlaneRes *res;
drmModePlane *p;
unsigned int i = 0;
/* check for valid device */
if ((!dev) || (dev->drm.fd < 0)) return EINA_FALSE;
/* get plane resources */
if (!(res = drmModeGetPlaneResources(dev->drm.fd))) return EINA_FALSE;
for (i = 0; i < res->count_planes; i++)
{
Ecore_Drm_Sprite *sprite;
if (!(p = drmModeGetPlane(dev->drm.fd, res->planes[i])))
continue;
/* allocate space for sprite */
if (!(sprite =
malloc(sizeof(Ecore_Drm_Sprite) +
((sizeof(unsigned int)) * p->count_formats))))
{
drmModeFreePlane(p);
continue;
}
sprite->drm_fd = dev->drm.fd;
sprite->crtcs = p->possible_crtcs;
sprite->plane_id = p->plane_id;
sprite->num_formats = p->count_formats;
memcpy(sprite->formats, p->formats,
p->count_formats * sizeof(p->formats[0]));
drmModeFreePlane(p);
dev->sprites = eina_list_append(dev->sprites, sprite);
}
/* free resources */
drmModeFreePlaneResources(res);
return EINA_TRUE;
}
EAPI void
ecore_drm_sprites_destroy(Ecore_Drm_Device *dev)
{
Ecore_Drm_Sprite *sprite;
/* check for valid device */
if (!dev) return;
EINA_LIST_FREE(dev->sprites, sprite)
{
ecore_drm_sprites_fb_set(sprite, 0, 0);
_ecore_drm_output_fb_release(sprite->output, sprite->current_fb);
_ecore_drm_output_fb_release(sprite->output, sprite->next_fb);
free(sprite);
}
}
EAPI void
ecore_drm_sprites_fb_set(Ecore_Drm_Sprite *sprite, int fb_id, int flags)
{
if ((!sprite) || (!sprite->output)) return;
if (fb_id)
{
drmModeSetPlane(sprite->drm_fd, sprite->plane_id,
sprite->output->crtc_id, fb_id, flags,
sprite->dest.x, sprite->dest.y, sprite->dest.w,
sprite->dest.h, sprite->src.x, sprite->src.y,
sprite->src.w, sprite->src.h);
}
else
{
drmModeSetPlane(sprite->drm_fd, sprite->plane_id,
sprite->output->crtc_id, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0);
}
}
EAPI Eina_Bool
ecore_drm_sprites_crtc_supported(Ecore_Drm_Output *output, unsigned int supported)
{
Ecore_Drm_Device *dev;
unsigned int c = 0;
dev = output->dev;
for (c = 0; c < dev->crtc_count; c++)
{
if (dev->crtcs[c] != output->crtc_id) continue;
if ((supported) && (1 << c)) return EINA_FALSE;
}
return EINA_TRUE;
}

View File

@ -0,0 +1,297 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "ecore_drm_private.h"
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/vt.h>
#include <linux/kd.h>
#ifndef KDSKBMUTE
# define KDSKBMUTE 0x4B51
#endif
static Eina_Bool
_ecore_drm_tty_cb_signal(void *data, int type EINA_UNUSED, void *event)
{
Ecore_Drm_Device *dev;
Ecore_Event_Signal_User *ev;
dev = data;
ev = event;
DBG("Caught user signal: %d", ev->number);
if (ev->number == 1)
{
Ecore_Drm_Input *input;
Ecore_Drm_Output *output;
Ecore_Drm_Sprite *sprite;
Eina_List *l;
DBG("Release VT");
/* disable inputs (suspends) */
EINA_LIST_FOREACH(dev->inputs, l, input)
ecore_drm_inputs_disable(input);
/* disable hardware cursor */
EINA_LIST_FOREACH(dev->outputs, l, output)
ecore_drm_output_cursor_size_set(output, 0, 0, 0);
/* disable sprites */
EINA_LIST_FOREACH(dev->sprites, l, sprite)
ecore_drm_sprites_fb_set(sprite, 0, 0);
/* close input fds ?? */
/* drop drm master */
if (ecore_drm_device_master_drop(dev))
{
/* issue ioctl to release vt */
if (!ecore_drm_tty_release(dev))
ERR("Could not release VT: %m");
}
else
ERR("Could not drop drm master: %m");
}
else if (ev->number == 2)
{
Ecore_Drm_Output *output;
Ecore_Drm_Input *input;
Eina_List *l;
DBG("Acquire VT");
/* issue ioctl to acquire vt */
if (ecore_drm_tty_acquire(dev))
{
/* set drm master */
if (!ecore_drm_device_master_set(dev))
ERR("Could not set drm master: %m");
/* set output mode */
EINA_LIST_FOREACH(dev->outputs, l, output)
ecore_drm_output_enable(output);
/* enable inputs */
EINA_LIST_FOREACH(dev->inputs, l, input)
ecore_drm_inputs_enable(input);
}
else
ERR("Could not acquire VT: %m");
}
return EINA_TRUE;
}
static Eina_Bool
_ecore_drm_tty_setup(Ecore_Drm_Device *dev)
{
struct stat st;
int kb_mode;
struct vt_mode vtmode = { 0 };
if (fstat(dev->tty.fd, &st) == -1)
{
ERR("Failed to get stats for tty: %m");
return EINA_FALSE;
}
if (ioctl(dev->tty.fd, KDGKBMODE, &kb_mode))
{
ERR("Could not get tty keyboard mode: %m");
return EINA_FALSE;
}
/* NB: Don't set this. This Turns OFF keyboard on the VT */
/* if (ioctl(dev->tty.fd, KDSKBMUTE, 1) && */
/* ioctl(dev->tty.fd, KDSKBMODE, K_OFF)) */
/* { */
/* ERR("Could not set K_OFF keyboard mode: %m"); */
/* return EINA_FALSE; */
/* } */
/* if (ioctl(dev->tty.fd, KDSETMODE, KD_GRAPHICS)) */
/* { */
/* ERR("Could not set graphics mode: %m"); */
/* return EINA_FALSE; */
/* } */
vtmode.mode = VT_PROCESS;
vtmode.waitv = 0;
vtmode.relsig = SIGUSR1;
vtmode.acqsig = SIGUSR2;
if (ioctl(dev->tty.fd, VT_SETMODE, &vtmode) < 0)
{
ERR("Could not set Terminal Mode: %m");
return EINA_FALSE;
}
/* if (ioctl(dev->tty.fd, VT_ACTIVATE, minor(st.st_rdev)) < 0) */
/* { */
/* ERR("Failed to activate vt: %m"); */
/* return EINA_FALSE; */
/* } */
/* if (ioctl(dev->tty.fd, VT_WAITACTIVE, minor(st.st_rdev)) < 0) */
/* { */
/* ERR("Failed to wait active: %m"); */
/* return EINA_FALSE; */
/* } */
return EINA_TRUE;
}
/**
* @defgroup Ecore_Drm_Tty_Group Tty manipulation functions
*
* Functions that deal with opening, closing, and otherwise using a tty
*/
/**
* Open a tty for use
*
* @param dev The Ecore_Drm_Device that this tty will belong to.
* @param name The name of the tty to try and open.
* If NULL, /dev/tty0 will be used.
*
* @return EINA_TRUE on success, EINA_FALSE on failure
*
* @ingroup Ecore_Drm_Tty_Group
*/
EAPI Eina_Bool
ecore_drm_tty_open(Ecore_Drm_Device *dev, const char *name)
{
char tty[32] = "<stdin>";
/* check for valid device */
if ((!dev) || (!dev->drm.name)) return EINA_FALSE;
/* assign default tty fd of -1 */
dev->tty.fd = -1;
if (!name)
{
char *env;
if ((env = getenv("ECORE_DRM_TTY")))
snprintf(tty, sizeof(tty), "%s", env);
else
dev->tty.fd = STDIN_FILENO;
}
else // FIXME: NB: This should Really check for format of name (/dev/xyz)
snprintf(tty, sizeof(tty), "%s", name);
if (dev->tty.fd < 0)
{
DBG("Trying to Open Tty: %s", tty);
dev->tty.fd = open(tty, O_RDWR | O_NOCTTY);
if (dev->tty.fd < 0)
{
DBG("Failed to Open Tty: %m");
return EINA_FALSE;
}
}
if (dev->tty.fd < 0)
{
DBG("Failed to open tty %s", tty);
return EINA_FALSE;
}
DBG("Opened Tty %s : %d", tty, dev->tty.fd);
/* save tty name */
dev->tty.name = eina_stringshare_add(tty);
/* FIXME */
if (!_ecore_drm_tty_setup(dev))
return EINA_FALSE;
/* setup handler for signals */
dev->tty.event_hdlr =
ecore_event_handler_add(ECORE_EVENT_SIGNAL_USER,
_ecore_drm_tty_cb_signal, dev);
/* set current tty into env */
setenv("ECORE_DRM_TTY", tty, 1);
return EINA_TRUE;
}
/**
* Close an already opened tty
*
* @param dev The Ecore_Drm_Device which owns this tty.
*
* @return EINA_TRUE on success, EINA_FALSE on failure
*
* @ingroup Ecore_Drm_Tty_Group
*/
EAPI Eina_Bool
ecore_drm_tty_close(Ecore_Drm_Device *dev)
{
/* check for valid device */
if ((!dev) || (!dev->drm.name)) return EINA_FALSE;
close(dev->tty.fd);
dev->tty.fd = -1;
/* destroy the event handler */
if (dev->tty.event_hdlr) ecore_event_handler_del(dev->tty.event_hdlr);
dev->tty.event_hdlr = NULL;
/* clear the tty name */
if (dev->tty.name) eina_stringshare_del(dev->tty.name);
dev->tty.name = NULL;
unsetenv("ECORE_DRM_TTY");
return EINA_TRUE;
}
/**
* Release a virtual terminal
*
* @param dev The Ecore_Drm_Device which owns this tty.
*
* @return EINA_TRUE on success, EINA_FALSE on failure
*
* @ingroup Ecore_Drm_Tty_Group
*/
EAPI Eina_Bool
ecore_drm_tty_release(Ecore_Drm_Device *dev)
{
/* check for valid device */
if ((!dev) || (!dev->drm.name) || (dev->tty.fd < 0)) return EINA_FALSE;
/* send ioctl for vt release */
if (ioctl(dev->tty.fd, VT_RELDISP, 1) < 0) return EINA_FALSE;
return EINA_TRUE;
}
/**
* Acquire a virtual terminal
*
* @param dev The Ecore_Drm_Device which owns this tty.
*
* @return EINA_TRUE on success, EINA_FALSE on failure
*
* @ingroup Ecore_Drm_Tty_Group
*/
EAPI Eina_Bool
ecore_drm_tty_acquire(Ecore_Drm_Device *dev)
{
/* check for valid device */
if ((!dev) || (!dev->drm.name) || (dev->tty.fd < 0)) return EINA_FALSE;
/* send ioctl for vt acquire */
if (ioctl(dev->tty.fd, VT_RELDISP, VT_ACKACQ) < 0) return EINA_FALSE;
return EINA_TRUE;
}