This is an old revision of the document!
Souce Code는 다음과 같다.
#include <stdio.h>
#include <stdint.h>
#include <string.h> /* for memset */
#include <unistd.h> /* for open/close .. */
#include <fcntl.h> /* for O_RDWR */
#include <sys/ioctl.h> /* for ioctl */
#include <sys/mman.h> /* for mmap */
#include <linux/fb.h> /* for fb_var_screeninfo, FBIOGET_VSCREENINFO */
#include "drm.h" /* /home/stephanos/VI/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/include/drm -> check Makefile */
#include "drm_mode.h"
#define DEFAULT_FRAME_BUFFER_FILE "/dev/fb0"
#define DEV_LCD_DRM "/dev/dri/card0"
#define MAXIMUM_NUMBER_OF_CONNECTORS 10
#define MAXIMUM_NUMBER_OF_CONNECTOR_RESOURCE_BUFFER 20
typedef struct
{
void * pBuffer;
uint32_t ByteSizeOfBuffer;
uint32_t ScreenWidth;
uint32_t ScreenHeight;
} FrameBuffer_t;
typedef struct
{
uint64_t pFB_ID[MAXIMUM_NUMBER_OF_CONNECTORS];
uint64_t pCRTC_ID[MAXIMUM_NUMBER_OF_CONNECTORS];
uint64_t pCONNECTOR_ID[MAXIMUM_NUMBER_OF_CONNECTORS];
uint64_t pENCODER_ID[MAXIMUM_NUMBER_OF_CONNECTORS];
} DRMResourceBuffer_t;
typedef struct
{
struct drm_mode_modeinfo pMode[MAXIMUM_NUMBER_OF_CONNECTOR_RESOURCE_BUFFER];
uint64_t pProps[MAXIMUM_NUMBER_OF_CONNECTOR_RESOURCE_BUFFER];
uint64_t PPropValue[MAXIMUM_NUMBER_OF_CONNECTOR_RESOURCE_BUFFER];
uint64_t pEncoder[MAXIMUM_NUMBER_OF_CONNECTOR_RESOURCE_BUFFER];
} DRMConnectorResourceBuffer_t;
typedef struct
{
struct drm_mode_create_dumb Create;
struct drm_mode_map_dumb Map;
struct drm_mode_fb_cmd Cmd;
} DRMdumbBuffer_t;
typedef struct
{
int Device;
void * pFrameBuffer;
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
} FrameBufferContext_t;
void drm_draw_pixel( FrameBuffer_t *pFrameBuffer,
uint32_t X,
uint32_t Y,
uint32_t Color/*(b << 16) | (g << 8) | (r << 0)*/)
{
if ( pFrameBuffer != (FrameBuffer_t *)0 )
{
if ((pFrameBuffer->ScreenWidth > X)
&& (pFrameBuffer->ScreenHeight > Y))
{
*(((uint32_t*)pFrameBuffer->pBuffer ) + Y * pFrameBuffer->ScreenWidth + X) = Color;
}
}
}
void drm_draw_test_pattern(FrameBuffer_t *pFrameBuffer)
{
uint32_t x;
uint32_t y;
uint32_t h;
uint32_t w;
h = pFrameBuffer->ScreenHeight;
w = pFrameBuffer->ScreenWidth;
for (y = 0; y < h; y++)
{
for (x = 0; x < w; x++)
{
if (x < 20 && y < 20)
{
drm_draw_pixel(pFrameBuffer, x, y, 0xffffff);
}
else if (x < 20 && (y > 20 && y < h - 20))
{
drm_draw_pixel(pFrameBuffer, x, y, 0xff);
}
else if (y < 20 && (x > 20 && x < w - 20))
{
drm_draw_pixel(pFrameBuffer, x, y, 0xff00);
}
else if (x > w - 20 && (y > 20 && y < h - 20))
{
drm_draw_pixel(pFrameBuffer, x, y, 0xff0000);
}
else if (y > h - 20 && (x > 20 && x < w - 20))
{
drm_draw_pixel(pFrameBuffer, x, y, 0xffff00);
}
else if (x == 20 || x == w - 20 ||
y == 20 || y == h - 20)
{
drm_draw_pixel(pFrameBuffer, x, y, 0xffffff);
}
else if (x == y || w - x == h - y)
{
drm_draw_pixel(pFrameBuffer, x, y, 0xff00ff);
}
else if (w - x == y || x == h - y)
{
drm_draw_pixel(pFrameBuffer, x, y, 0x00ffff);
}
else if (x > 20 && y > 20 && x < w - 20 && y < h - 20)
{
int t = x * 3 / w;
unsigned r = 0, g = 0, b = 0;
unsigned c;
if (t == 0)
{
b = (y % 256);
}
else if (t == 1)
{
g = (y % 256);
}
else if (t == 2)
{
r = (y % 256);
}
c = (b << 16) | (g << 8) | (r << 0);
drm_draw_pixel(pFrameBuffer, x, y, c);
}
else
{
drm_draw_pixel(pFrameBuffer, x, y, 0);
}
}
}
}
void drm_fill_screen(FrameBuffer_t *pFrameBuffer,
uint32_t Color/*(b << 16) | (g << 8) | (r << 0)*/)
{
uint32_t X;
uint32_t Y;
if ( pFrameBuffer != (FrameBuffer_t *)0 )
{
for (Y = 0; Y < pFrameBuffer->ScreenHeight; Y++)
{
for (X = 0; X < pFrameBuffer->ScreenWidth; X++)
{
*(((uint32_t*)pFrameBuffer->pBuffer ) + Y * pFrameBuffer->ScreenWidth + X) = Color;
}
}
}
}
void fb_draw_pixel( FrameBufferContext_t *pFrameBufferContext,
uint32_t X,
uint32_t Y,
uint32_t Color)
{
void * pFrameBuffer;
pFrameBuffer = pFrameBufferContext->pFrameBuffer;
if (pFrameBufferContext->var.bits_per_pixel == 8)
{
unsigned char * p;
pFrameBuffer += pFrameBufferContext->fix.line_length * Y;
p = pFrameBuffer;
p += X;
*p = (unsigned char)Color;
}
else if (pFrameBufferContext->var.bits_per_pixel == 16)
{
unsigned short * p;
pFrameBuffer += pFrameBufferContext->fix.line_length * Y;
p = pFrameBuffer;
p += X;
*p = (unsigned short)(((((Color >> 16) & 0xff) * 32 / 256) << 11) | ((((Color >> 8) & 0xff) * 64 / 256) << 5) | ((((Color >> 0) & 0xff) * 32 / 256) << 0));
}
else if (pFrameBufferContext->var.bits_per_pixel == 24)
{
unsigned char * p;
p = (unsigned char *)pFrameBuffer + pFrameBufferContext->fix.line_length * Y + 3 * X;
*p++ = (unsigned char)Color;
*p++ = (unsigned char)(Color >> 8);
*p = (unsigned char)(Color >> 16);
}
else
{
uint32_t * p;
pFrameBuffer += pFrameBufferContext->fix.line_length * Y;
p = pFrameBuffer;
p += X;
*p = Color;
}
}
void fill_screen(FrameBufferContext_t *pFrameBufferContext)
{
uint32_t x;
uint32_t y;
uint32_t h;
uint32_t w;
h = pFrameBufferContext->var.yres;
w = pFrameBufferContext->var.xres;
for (y = 0; y < h; y++)
{
for (x = 0; x < w; x++)
{
if (x < 20 && y < 20)
{
fb_draw_pixel(pFrameBufferContext, x, y, 0xffffff);
}
else if (x < 20 && (y > 20 && y < h - 20))
{
fb_draw_pixel(pFrameBufferContext, x, y, 0xff);
}
else if (y < 20 && (x > 20 && x < w - 20))
{
fb_draw_pixel(pFrameBufferContext, x, y, 0xff00);
}
else if (x > w - 20 && (y > 20 && y < h - 20))
{
fb_draw_pixel(pFrameBufferContext, x, y, 0xff0000);
}
else if (y > h - 20 && (x > 20 && x < w - 20))
{
fb_draw_pixel(pFrameBufferContext, x, y, 0xffff00);
}
else if (x == 20 || x == w - 20 ||
y == 20 || y == h - 20)
{
fb_draw_pixel(pFrameBufferContext, x, y, 0xffffff);
}
else if (x == y || w - x == h - y)
{
fb_draw_pixel(pFrameBufferContext, x, y, 0xff00ff);
}
else if (w - x == y || x == h - y)
{
fb_draw_pixel(pFrameBufferContext, x, y, 0x00ffff);
}
else if (x > 20 && y > 20 && x < w - 20 && y < h - 20)
{
int t = x * 3 / w;
unsigned r = 0, g = 0, b = 0;
unsigned c;
if (pFrameBufferContext->var.bits_per_pixel == 16)
{
if (t == 0)
{
b = (y % 32) * 256 / 32;
}
else if (t == 1)
{
g = (y % 64) * 256 / 64;
}
else if (t == 2)
{
r = (y % 32) * 256 / 32;
}
}
else
{
if (t == 0)
{
b = (y % 256);
}
else if (t == 1)
{
g = (y % 256);
}
else if (t == 2)
{
r = (y % 256);
}
}
c = (b << 16) | (g << 8) | (r << 0);
fb_draw_pixel(pFrameBufferContext, x, y, c);
}
else
{
fb_draw_pixel(pFrameBufferContext, x, y, 0);
}
}
}
}
void clear_area( FrameBufferContext_t *pFrameBufferContext,
uint32_t X,
uint32_t Y,
uint32_t W,
uint32_t H)
{
uint32_t AddressOffsetForH;
uint32_t CountForH;
unsigned char * pFrameBuffer;
pFrameBuffer = (unsigned char *)pFrameBufferContext->pFrameBuffer;
for (CountForH = 0; CountForH < H; CountForH++)
{
AddressOffsetForH = (X + pFrameBufferContext->var.xoffset) * (pFrameBufferContext->var.bits_per_pixel / 8)
+ (Y + CountForH + pFrameBufferContext->var.yoffset) * pFrameBufferContext->fix.line_length;
memset(pFrameBuffer + AddressOffsetForH, 0, W * pFrameBufferContext->var.bits_per_pixel / 8);
}
}
int device_open_mode(FrameBufferContext_t *pFrameBufferContext, int BitPerPixel)
{
pFrameBufferContext->Device = open(DEFAULT_FRAME_BUFFER_FILE, O_RDWR);
if(pFrameBufferContext->Device < 0)
{
printf("Faile to open : %s \n", DEFAULT_FRAME_BUFFER_FILE);
return -1;
}
if(ioctl(pFrameBufferContext->Device, FBIOGET_VSCREENINFO, &pFrameBufferContext->var) < 0)
{
printf("fbdev ioctl(VSCREENINFO)\n");
close(pFrameBufferContext->Device);
return -1;
}
if(ioctl(pFrameBufferContext->Device, FBIOGET_FSCREENINFO, &pFrameBufferContext->fix) < 0)
{
printf("fbdev ioctl(FSCREENINFO)\n");
close(pFrameBufferContext->Device);
return -1;
}
if(pFrameBufferContext->var.bits_per_pixel != BitPerPixel)
{
pFrameBufferContext->var.bits_per_pixel = BitPerPixel;
if(ioctl(pFrameBufferContext->Device, FBIOPUT_VSCREENINFO, &pFrameBufferContext->var) < 0)
{
printf("fbdev ioctl(PUT)\n");
close(pFrameBufferContext->Device);
return -1;
}
}
return 0;
}
int device_open(FrameBufferContext_t *pFrameBufferContext)
{
if(device_open_mode(pFrameBufferContext, 8) != 0)
{
printf("Faile to open with 8bit: %s \n", DEFAULT_FRAME_BUFFER_FILE);
return -1;
}
close(pFrameBufferContext->Device);
if(device_open_mode(pFrameBufferContext, 24) != 0)
{
printf("Faile to open with 24bit: %s \n", DEFAULT_FRAME_BUFFER_FILE);
return -1;
}
printf("x-resolution : %d\n", pFrameBufferContext->var.xres);
printf("y-resolution : %d\n", pFrameBufferContext->var.yres);
printf("x-resolution(virtual) : %d\n", pFrameBufferContext->var.xres_virtual);
printf("y-resolution(virtual) : %d\n", pFrameBufferContext->var.yres_virtual);
printf("bpp : %d\n", pFrameBufferContext->var.bits_per_pixel);
printf("length of frame buffer memory : %d\n", pFrameBufferContext->fix.smem_len);
return 0;
}
static int test_simple_fb_style(void)
{
FrameBufferContext_t FrameBufferContext;
int Error;
printf("Opening frame buffer device `%s'\n", DEFAULT_FRAME_BUFFER_FILE);
if(device_open(&FrameBufferContext) != 0)
{
printf("Fail to Device Open\n");
return 0;
}
FrameBufferContext.pFrameBuffer = mmap(0,
FrameBufferContext.var.yres_virtual * FrameBufferContext.fix.line_length,
PROT_WRITE | PROT_READ,
MAP_SHARED,
FrameBufferContext.Device,
0);
if(FrameBufferContext.pFrameBuffer == MAP_FAILED)
{
printf("Frame buffer MAP fail !\n");
close(FrameBufferContext.Device);
return 0;
}
/* Display pattern to screen */
fill_screen(&FrameBufferContext);
/* End of... */
munmap( FrameBufferContext.pFrameBuffer,
FrameBufferContext.var.yres_virtual * FrameBufferContext.fix.line_length);
close(FrameBufferContext.Device);
return 0;
}
static int drm_open(void)
{
int Device;
int Flags;
uint64_t HasDumbBuffer;
Device = -1;
if ( access(DEV_LCD_DRM, F_OK) )
{
printf("no device - %s\n", DEV_LCD_DRM);
return 0;
}
if ( (Device = open(DEV_LCD_DRM, O_RDWR)) <= 0 )
{
printf("Faile to open : %s \n", DEV_LCD_DRM);
return 0;
}
// set FD_CLOEXEC flag
Flags = fcntl(Device, F_GETFD);
if (Flags < 0)
{
printf("Faile to read Flags \n");
close(Device);
return 0;
}
if ( fcntl(Device, F_SETFD, Flags | FD_CLOEXEC) < 0 )
{
printf("Faile to set Falsgs FD_CLOEXEC \n");
close(Device);
return 0;
}
return Device;
}
static int drm_get_frame_buffer(FrameBuffer_t *pFrameBuffer,
int Device,
uint64_t * pConnectorID)
{
struct drm_mode_get_connector ConnectorResource = {0};
DRMConnectorResourceBuffer_t DRMConnectorResourceBuffer = {0};
DRMdumbBuffer_t DRMdumbBuffer = {0};
struct drm_mode_get_encoder Encoder = {0};
struct drm_mode_crtc CRTC = {0};
if ((pFrameBuffer != (FrameBuffer_t *)0)
&& (pConnectorID != (uint64_t * )0) )
{
// get connector resource counts
ConnectorResource.connector_id = *pConnectorID;
ioctl(Device, DRM_IOCTL_MODE_GETCONNECTOR, &ConnectorResource);
// get connector resources
ConnectorResource.modes_ptr = (uint64_t)DRMConnectorResourceBuffer.pMode;
ConnectorResource.props_ptr = (uint64_t)DRMConnectorResourceBuffer.pProps;
ConnectorResource.prop_values_ptr = (uint64_t)DRMConnectorResourceBuffer.PPropValue;
ConnectorResource.encoders_ptr = (uint64_t)DRMConnectorResourceBuffer.pEncoder;
ioctl(Device, DRM_IOCTL_MODE_GETCONNECTOR, &ConnectorResource);
// Check if the connector is OK to use (connected to something)
if ((ConnectorResource.count_encoders > 0)
&& (ConnectorResource.count_modes > 0)
&& (ConnectorResource.encoder_id != 0)
&& (ConnectorResource.connection != 0) )
{
// Creating a dumb buffer
// If we create the buffer later, we can get the size of the screen first.
// This must be a valid mode, so it's probably best to do this after we find a valid crtc with modes.
DRMdumbBuffer.Create.width = DRMConnectorResourceBuffer.pMode[0].hdisplay;
DRMdumbBuffer.Create.height = DRMConnectorResourceBuffer.pMode[0].vdisplay;
DRMdumbBuffer.Create.bpp = 32;
DRMdumbBuffer.Create.flags = 0; /* must be zero */
DRMdumbBuffer.Create.pitch = 0;
DRMdumbBuffer.Create.size = 0;
DRMdumbBuffer.Create.handle = 0;
if(ioctl(Device, DRM_IOCTL_MODE_CREATE_DUMB, &DRMdumbBuffer.Create) < 0)
{
printf("Fail to create dumb buffer\n");
};
DRMdumbBuffer.Cmd.width=DRMdumbBuffer.Create.width;
DRMdumbBuffer.Cmd.height=DRMdumbBuffer.Create.height;
DRMdumbBuffer.Cmd.bpp=DRMdumbBuffer.Create.bpp;
DRMdumbBuffer.Cmd.pitch=DRMdumbBuffer.Create.pitch;
DRMdumbBuffer.Cmd.depth=24;
DRMdumbBuffer.Cmd.handle=DRMdumbBuffer.Create.handle;
ioctl(Device,DRM_IOCTL_MODE_ADDFB,&DRMdumbBuffer.Cmd);
DRMdumbBuffer.Map.handle=DRMdumbBuffer.Create.handle;
ioctl(Device,DRM_IOCTL_MODE_MAP_DUMB,&DRMdumbBuffer.Map);
pFrameBuffer->pBuffer = mmap(0, DRMdumbBuffer.Create.size, PROT_READ | PROT_WRITE, MAP_SHARED, Device, DRMdumbBuffer.Map.offset);
pFrameBuffer->ByteSizeOfBuffer = DRMdumbBuffer.Create.size;
pFrameBuffer->ScreenWidth = DRMdumbBuffer.Create.width;
pFrameBuffer->ScreenHeight = DRMdumbBuffer.Create.height;
// drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
// Kernel Mode Setting (KMS)
printf("Connection %d : mode: %d, prop: %d, enc: %d\n",
ConnectorResource.connection,
ConnectorResource.count_modes,
ConnectorResource.count_props,
ConnectorResource.count_encoders);
printf("modes: %dx%d Frame Buffer Address: 0x%X Frame Buffer Byte Size: 0x%X(%d) \n",
DRMConnectorResourceBuffer.pMode[0].hdisplay,
DRMConnectorResourceBuffer.pMode[0].vdisplay,
pFrameBuffer->pBuffer,
pFrameBuffer->ByteSizeOfBuffer, pFrameBuffer->ByteSizeOfBuffer);
// get encoder
Encoder.encoder_id = ConnectorResource.encoder_id;
ioctl(Device, DRM_IOCTL_MODE_GETENCODER, &Encoder);
CRTC.crtc_id = Encoder.crtc_id;
ioctl(Device, DRM_IOCTL_MODE_GETCRTC, &CRTC);
CRTC.fb_id = DRMdumbBuffer.Cmd.fb_id;
CRTC.set_connectors_ptr = (uint64_t)pConnectorID;
CRTC.count_connectors = 1;
CRTC.mode = DRMConnectorResourceBuffer.pMode[0];
CRTC.mode_valid = 1;
ioctl(Device, DRM_IOCTL_MODE_SETCRTC, &CRTC);
return 0;
}
else
{
printf("Not connected\n");
}
}
return -1;
}
static int drm_free_frame_buffer(FrameBuffer_t *pFrameBuffer)
{
if (pFrameBuffer != (FrameBuffer_t *)0)
{
if (pFrameBuffer->ByteSizeOfBuffer != 0)
{
munmap( pFrameBuffer->pBuffer, pFrameBuffer->ByteSizeOfBuffer );
printf("Free Frame Buffer\n");
return 0;
}
}
return -1;
}
static int test_drm_style(void)
{
int Device;
uint32_t ConnectorIndex;
uint32_t Color;
struct drm_mode_card_res Resource = {0};
DRMResourceBuffer_t DRMResourceBuffer = {0};
Device = drm_open();
if ( Device != 0 )
{
FrameBuffer_t pFrameBuffer[MAXIMUM_NUMBER_OF_CONNECTORS];
printf("DRM opened !\n");
// Kernel Mode Setting (KMS)
// Become the "master" of the DRI device
ioctl(Device, DRM_IOCTL_SET_MASTER, 0);
printf("DRM DRM_IOCTL_SET_MASTER\n");
// Get resource counts
ioctl(Device, DRM_IOCTL_MODE_GETRESOURCES, &Resource);
Resource.fb_id_ptr = (uint64_t)DRMResourceBuffer.pFB_ID;
Resource.crtc_id_ptr = (uint64_t)DRMResourceBuffer.pCRTC_ID;
Resource.connector_id_ptr = (uint64_t)DRMResourceBuffer.pCONNECTOR_ID;
Resource.encoder_id_ptr = (uint64_t)DRMResourceBuffer.pENCODER_ID;
// Get resource IDs
ioctl(Device, DRM_IOCTL_MODE_GETRESOURCES, &Resource);
printf("Get Resource ID fb: %d, crtc: %d, conn: %d, enc: %d\n",
Resource.count_fbs,
Resource.count_crtcs,
Resource.count_connectors,
Resource.count_encoders);
// Loop though all available connectors
for (ConnectorIndex = 0; ConnectorIndex < Resource.count_connectors; ConnectorIndex++)
{
drm_get_frame_buffer(&pFrameBuffer[ConnectorIndex], Device, &DRMResourceBuffer.pCONNECTOR_ID[ConnectorIndex]);
}
// Stop being the "master" of the DRI device
ioctl(Device, DRM_IOCTL_DROP_MASTER, 0);
printf("DRM DRM_IOCTL_DROP_MASTER\n");
int j;
for (j = 0; j < 100; j++)
{
for (ConnectorIndex = 0; ConnectorIndex < Resource.count_connectors; ConnectorIndex++)
{
drm_fill_screen(&pFrameBuffer[ConnectorIndex], 0x00000000);
usleep(100000);
Color = (rand() % 0x00ffffff) & 0x00ff00ff;
drm_fill_screen(&pFrameBuffer[ConnectorIndex], Color);
usleep(100000);
for (uint32_t k = 0; k <255; k++)
{
drm_fill_screen(&pFrameBuffer[ConnectorIndex], (Color | (k << 24)));
}
drm_draw_test_pattern(&pFrameBuffer[ConnectorIndex]);
usleep(100000);
}
}
//DRM_IOCTL_MODE_DESTROY_DUMB
for (ConnectorIndex = 0; ConnectorIndex < Resource.count_connectors; ConnectorIndex++)
{
drm_free_frame_buffer(&pFrameBuffer[ConnectorIndex]);
}
close(Device);
printf("DRM closed !\n");
}
}
int main()
{
printf("Test Symple FB Style\n");
test_simple_fb_style();
printf("Test DRM Style\n");
test_drm_style();
return 0;
}
아래와 같이 컴파일 한다.
stephanos@stephanos-VirtualBox:~/VI/App$ /home/stephanos/VI/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc -I$/home/stephanos/VI/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/include/drm -o test test.c
NFS에 위에서 만들어진 test를 복사한다. Target Board를 NFS로 부팅한다.
로그인 후 아래와 같이 실행한다.