In this article: Isn’t the whole system just a database? – libdrm, I explored a little bit of the database nature of Linux by using libudev to enumerate and open libdrm devices. After that, I spent some time bringing up a USB module: LJIT2libusb. libusb is a useful cross platform library that makes it relatively easy to gain access to the usb functions on multiple platforms. It can enumerate devices, deal with hot plug notifications, open up, read, write, etc.
At its core, on Linux at least, libusb tries to leverage the uvdev capabilities of the target system, if those capabilities are there. This means that device enumeration and hot plugging actually use the libuvdev stuff. In fact, the code for enumerating those usb devices in libusb looks like this:
udev_enumerate_add_match_subsystem(enumerator, "usb"); udev_enumerate_add_match_property(enumerator, "DEVTYPE", "usb_device"); udev_enumerate_scan_devices(enumerator); devices = udev_enumerate_get_list_entry(enumerator);
There’s more stuff of course, to turn that into data structures which are appropriate for use within the libusb view of the world. But, here’s the equivalent using LLUI and the previously developed UVDev stuff:
local function isUsbDevice(dev) if dev.IsInitialized and dev:getProperty("subsystem") == "usb" and dev:getProperty("devtype") == "usb_device" then return true; end return false; end each(print, filter(isUsbDevice, ctxt:devices()))
It’s just illustrative, but it’s fairly simple to understand I think. The ‘ctxt:devices()’ is an iterator over all the devices in the system. The ‘filter’ function is part of the luafun functional programming routines available to Lua. the ‘isUsbDevice’ is a predicate function, which returns ‘true’ when the device in question matches what it believes makes a device a ‘usb’ device. In this case, its the subsystem and dev_type properties which are used.
Being able to easily query devices like this makes life a heck of a lot easier. No funky code polluting my pure application. Just these simple query predicates written in Lua, and I’m all set. So, instead of relying on libusb to enumerate my usb devices, I can just enumerate them directly using uvdev, which is what the library does anyway. Enumeration and hotplug handing is part of the library. The other part is the actual send and receiving of data. For that, the libusb library is still primarily important, as replacing that code will take some time.
Where else can this great query capability be applied? Well, libudev is just a nice wrapper atop sysfs, which is that virtual file system built into Linux for gaining access to device information and control of the same. There’s all sorts of stuff in there. So, let’s say you want to list all the block devices?
local function isBlockDevice(dev) if dev.IsInitialized and dev:getProperty("subsystem") == "block" then return true; end return false; end
That will get all the devices which are in the subsystem “block”. That includes physical disks, virtual disks, partitions, and the like. If you’re after just the physical ones, then you might use something like this:
local function isPhysicalBlockDevice(dev) if dev.IsInitialized and dev:getProperty("subsystem") == "block" and dev:getProperty("devtype") == "disk" and dev:getProperty("ID_BUS") ~= nil then return true; end return false; end
Here, a physical device is indicated by subsystem == ‘block’ and devtype == ‘disk’ and the ‘ID_BUS’ property exists, assuming any physical disk would show up on one of the system’s buses. This won’t catch a SD card though. For that, you’d use the first one, and then look for a property related to being an SD card. Same goes for ‘cd’ vs ramdisk, or whatever. You can make these queries as complex or simple as you want.
Once you have a device, you can simply open it using the “SysName” parameter, handed to an fopen() call.
I find this to be a great way to program. It makes the creation of utilities such as ‘lsblk’ relatively easy. You would just look for all the block devices and their partitions, and put them into a table. Then separately, you would have a display routine, which would consume the table and generate whatever output you want. I find this much better than the typical Linux tools which try to do advanced display using the terminal window. That’s great as far as it goes, but not so great if what you really want is a nice html page generated for some remote viewing.
At any rate, this whole libudev exploration is a great thing. You can list all devices easily, getting every bit of information you care to examine. Since it’s all scriptable, it’s fairly easy to taylor your queries on the fly, looking at, discovering, and the like. I discovered that the thumb print reader in my old laptop was made by Broadcom, and my webcam by 3M? It’s just so much fun.
Well there you have it. The more you spelunk, the more you know, and the more you can fiddle about.
And what’s this then? It’s the draw_crtc_lines.lua example from the LJIT2RenderingManager repository. I am using a LuaJIT wrapped libdrm to draw graphics on the console of my Linux machine. Yah, the console, where text is usually the norm. But, really, text is just a bunch of glyphs, which is nothing but a bunch of graphics right?
That was three years ago. With the Raspberry Pi, it was actually a fairly straight forward thing to go get a handle on the screen, and thus a pointer to the frame buffer data. Just a couple of function calls…
With the image here, I am using libdrm, which is at the core of graphics on Linux systems. The way it works is, you have fairly low level access to the graphics subsystem within the Linux kernel itself. Then there’s this userspace code, as represented by libdrm, which wraps up various IOCtl() calls to make it easy to interact with those kernel functions. Then other things, from console to X11 windows, are built atop this very lowest level. Throw in some libudev, libevdev, for input device handling, and you begin to have a UI subsystem.
Using libdrm isn’t particularly hard, but it’s pretty darned hard to find any crisp clear example articles or code to show you the way. Most of the examples are associated with mode setting, or fairly low level tests, or very out of date. I’m not quire sure why that is, other than the fact that most people don’t really care about this lowest level stuff, they just use the higher level frameworks such as Qt, SDL, and what have you. I was hard pressed to find anything like “how to draw to the current screen’s frame buffer directly”. So, here’s my version:
--[[ Draw on the frame buffer of the current default crtc The way to go about drawing to the current screen, without changing modes is: Create a card From that card, get the first connection that's actually connected to something From there, get the encoder it's using From there, get the crt controller associated with the encoder From there, get the framebuffer associated with the controller From there, we have the width, height, pitch, and data ptr So, that's enough to do some drawing --]] package.path = package.path..";../?.lua" local ffi = require("ffi") local bit = require("bit") local bor, band, lshift, rshift = bit.bor, bit.band, bit.lshift, bit.rshift local libc = require("libc") local utils = require("utils") local ppm = require("ppm") local DRMCard = require("DRMCard") local function RGB(r,g,b) return band(0xFFFFFF, bor(lshift(r, 16), lshift(g, 8), b)) end local function drawLines(fb) local color = RGB(23, 250, 127) for i = 1, 400 do utils.h_line(fb, 10+i, 10+i, i, color) end end local card, err = DRMCard(); local fb = card:getDefaultFrameBuffer(); fb.DataPtr = fb:getDataPtr(); print("fb: [bpp, depth, pitch]: ", fb.BitsPerPixel, fb.Depth, fb.Pitch) local function drawRectangles(fb) utils.rect(fb, 200, 200, 320, 240, RGB(230, 34, 127)) end local function draw() drawLines(fb) drawRectangles(fb); end draw(); ppm.write_PPM_binary("draw_crtc_lines.ppm", fb.DataPtr, fb.Width, fb.Height, fb.Pitch)
There, clear as can be right? It is actually fairly easy once you have the right wrappers and an understanding of how these things are supposed to work. The comment at the top of the code block explains the steps that go into it. Basically, you get a ‘card’ which is a representation of the video card in question. In this case, we just grab the first available card. If there are two in a system, you could be more specific about it. From the card, there are connectors (like VGA, DVI, HDMI, etc). So, you choose one of those, and ultimately you find out which frame buffer is associated with it. From there, you can ask the frame buffer for it’s data pointer, and then you’re ready to go.
Asking for the data pointer from the frame buffer looks like this:
function DRMFrameBuffer.getDataPtr(self) local fd = self.CardFd; local fbcmd = ffi.new("struct drm_mode_fb_cmd"); fbcmd.fb_id = self.Id; local res = xf86drm.drmIoctl(fd, drm.DRM_IOCTL_MODE_GETFB, fbcmd) if res < 0 then return nil, "drmIoctl DRM_IOCTL_MODE_GETFB failed"; end local mreq = ffi.new("struct drm_mode_map_dumb"); mreq.handle = fbcmd.handle; if (xf86drm.drmIoctl(self.CardFd, drm.DRM_IOCTL_MODE_MAP_DUMB, mreq) ~= 0) then error("drmIoctl DRM_IOCTL_MODE_MAP_DUMB failed"); end local size = fbcmd.pitch*fbcmd.height; local dataPtr = emmap(nil, size, bor(libc.PROT_READ, libc.PROT_WRITE), libc.MAP_SHARED, fd, mreq.offset); return dataPtr end
Well, that’s certainly a mouthful, just to get a pointer to the screen’s frame buffer data! The way it breaks down actually isn’t that bad. First, you do that drmIoctl to get a handle on the framebuffer. This is just how the kernel knows which framebuffer you’re talking about, because there can be many. Then you use that handle to get some information which is useful for doing an mmap() call (the offset from the beginning of the ‘file’ which represents the frame buffer). And finally, you make the mmap call so that you can get a pointer to the actual data of interest.
At this point, you now have a pointer to the data portion of the framebuffer, and you can start drawing to your heart’s content. Yep, it’s that easy.
Well this is all fine and dandy then. If you don’t feel like creating your own graphics rendering engine from scratch, then you could enlist the power of libpixman (LJIT2pixman – Drawing on Linux), another low level portion of the graphics pipeline on Linux. With libpixman, you can take this pointer you got from the framebuffer, and give it to one of the pixman_image_create_bits() calls, to get yourself a pixman drawing canvas, then you’re all set to use all the goodness pixman has to offer.
That’s very nifty, and this is how windowing systems are born.
Using libdrm can be a daunting task to the uninitiated (such as myself). Through nice Lua wrapping, and a bit of objectification, it can be tamed, and drawing on the screen is no harder than drawing into any other bit of memory you might have. One added bonus here. Since you have a pointer to the colors on the screen, doing a screencast capture, or desktop sharing, can’t be too far away…