Sending pointer to a struct through a queue in FreeRTOS - c

I can't seem to figure out how to send a pointer to a struct using a queue in FreeRTOS. I've tried all I could think of, yet I always get a pointer to some random region of memory.
I'm trying to send a pointer to a button struct to another task, where it will then be drawn on the screen. I tried sending the whole object and it worked, but since there's a lot of data in the struct (data of two icons) I don't really want to do that.
The code is being run in Atmel SAME70 Xplained.
Here is a simpler version of the code I'm working on:
typedef struct {
uint32_t width;
uint32_t height;
uint32_t x;
uint32_t y;
uint8_t status;
void (*callback)(t_but);
tImage iconOn;
tImage iconOff;
} t_but;
void task_lcd(void) {
xQueueButtons = xQueueCreate(6, sizeof(struct t_but *));
t_but *button;
configure_lcd();
draw_screen();
while (1) {
if (xQueueReceive(xQueueButtons, &(button), (TickType_t)500 / portTICK_PERIOD_MS)) {
// This always prints some random numbers.
printf("Button X: %" PRIu32 "\r\n", button->x);
}
}
}
void task_buttons(void) {
t_but butPlay = {.width = 64,
.height = 64,
.x = 60,
.y = 445,
.status = 0,
.callback = &ButPlayCallback,
.iconOn = play_red,
.iconOff = play_black};
xQueueSend(xQueueButtons, &butPlay, 0);
while (1) {
// Some other code.
}
}
Any help is very much appreciated.

It appears from the API that the xQueueSend does a copy via the pointer passed so if you want to pass a pointer on the queue you need to pass the address of a pointer that is pointing at your structure.
void task_buttons(void) {
t_but butPlay = {.width = 64,
.height = 64,
.x = 60,
.y = 445,
.status = 0,
.callback = &ButPlayCallback,
.iconOn = play_red,
.iconOff = play_black};
t_but * const p_but = &butPlay;
xQueueSend(xQueueButtons, &p_but, 0);
while (1) {
// Some other code.
}
}

Related

structure initialization for xh711 issue

I am building a program for Xh711 on esp32. I have defined a a struct and i am trying to initialize it. I am getting the next error.
could not convert '{GPIO_NUM_18, GPIO_NUM_19, 0, 1, 1, GAIN64, 0, -1,
storm->ad_str_t::smoothed, storm->ad_str_t::sampling,
storm->ad_str_t::timerHandle}' from '' to 'ad_xh711_handle_t'
void str_init_Xh711(ad_str_t *storm){
printf("Starting xh711...\n");
ad_xh711_handle_t xh711={
.dout= GPIO_NUM_18,
.scl= GPIO_NUM_19,
.offset = 0,
.calibrationFactor = 1,//NULL,
.conversionFactor=1,
.gainValue= Gain::GAIN64,
.rawData =0,//NULL,
.value = -1,
.smoothed =storm->smoothed,
.sampling =storm->sampling,
.timerHandle = storm->timerHandle
};
xh711_init(&xh711,Gain::GAIN64);
}
typedef struct {
gpio_num_t dout;
gpio_num_t scl;
int32_t offset = 0;
double calibrationFactor;
double conversionFactor=1;
Gain gainValue;
int32_t rawData;
double value = -1;
bool smoothed;
uint8_t sampling;
esp_timer_handle_t timerHandle;
}ad_xh711_handle_t;
default values in typedef can not work and have to be removed

C pointer array with elements of other pointer array

I have 2x structs, one for color (PixelColor) and the second for holding an array of available colors (Palette).
typedef struct{
int r;
int g;
int b;
int a;
}PixelColor;
typedef struct{
int size;
PixelColor *palette;
}Palette;
One game's global palette and one for objects that reference colors in the global palette.
PixelColor ShovelKnightColors[] = {
{0, 0, 0, 255},
{44, 44, 44, 255},
{96, 96, 96, 255},
{200, 192, 192, 255},
{0, 64, 88, 255},
...
};
Palette GamePalette = {59, ShovelKnightColors};
PixelColor CharacterColors[4];
//This doesn't work
CharacterColors[0] = GamePalette.palette[0];
CharacterColors[1] = GamePalette.palette[17];
CharacterColors[2] = GamePalette.palette[24];
CharacterColors[3] = GamePalette.palette[37];
Palette CharacterPalette = {4, CharactersColors};
I'm probably missing a fundamental thing, but I tried any idea I had. As an example:
PixelColor CharacterColors[] = {
GamePalette.palette[0],
GamePalette.palette[17],
GamePalette.palette[24],
GamePalette.palette[37]
}
All this is outside the main function, just to learn things I don't know about initiation. Please suggest a way to get closest to the initial idea of referencing the same values, because the goal is to create an ESP32 microcontroller project.
The problem with your code has to do with the fact that you have arrays inside your structs, and arrays need to be initialized.
My suggestion would be to create a FillPalette() function that would do the appropriate memory allocation each time:
void FillPalette(Palette *p, int size, PixelColor pixelColors[])
{
p->size = size;
p->palette = malloc( sizeof(PixelColor) * size );
for(int i = 0; i < size; ++i)
{
p->palette[ i ].r = pixelColors[ i ].r;
p->palette[ i ].g = pixelColors[ i ].g;
p->palette[ i ].b = pixelColors[ i ].b;
p->palette[ i ].a = pixelColors[ i ].a;
}
}
Another possibility, in order to avoid the for loop, is to use memcopy once the array has been initialized.
Anyway, remember to clear the palette when is not going to be used anymore:
void ClearPalette(Palette *p)
{
p->size = 0;
free( p->palette );
}

How to get the raw data of CR2 image using LibRaw(c++)

LibRaw is a library for reading RAW files from digital photo cameras (CRW/CR2, NEF, RAF, DNG, MOS, KDC, DCR, etc.; virtually all RAW formats are supported).
I want to know how to use LibRaw to get the raw data of a Canon CR2 image.
typedef struct
{
ushort (*image)[4] ;
libraw_image_sizes_t sizes;
libraw_iparams_t idata;
libraw_lensinfo_t lens;
libraw_makernotes_t makernotes;
libraw_shootinginfo_t shootinginfo;
libraw_output_params_t params;
unsigned int progress_flags;
unsigned int process_warnings;
libraw_colordata_t color;
libraw_imgother_t other;
libraw_thumbnail_t thumbnail;
libraw_rawdata_t rawdata;
void *parent_class;
} libraw_data_t;
typedef struct
{
void *raw_alloc;
ushort *raw_image;
ushort (*color4_image)[4] ;
ushort (*color3_image)[3];
float *float_image;
float (*float3_image)[3];
float (*float4_image)[4];
short (*ph1_cblack)[2];//
short (*ph1_rblack)[2];//
libraw_iparams_t iparams;//
libraw_image_sizes_t sizes;//
libraw_internal_output_params_t ioparams;//
libraw_colordata_t color;//
} libraw_rawdata_t;
This is the data structure of RAW data, I don't know which structure the most primitive data is stored in.
I have already got the answer, now download it below, I hope to help those in need.
int i,ret,verbose = 0,output_thumbs = 0;
char outfn [1024],thumbfn [1024];
//Create object
LibRaw RawProcessor;
putenv((char *)"TZ = UTC+8");
//
#define P1 RawProcessor.imgdata.idata
#define S RawProcessor.imgdata.sizes
#define C RawProcessor.imgdata.color
#define T RawProcessor.imgdata.thumbnail
#define P2 RawProcessor.imgdata.other
#define OUT RawProcessor.imgdata.params
OUT.output_tiff = 0; //
OUT.no_auto_scale=1;//
OUT.no_auto_bright=1;//
OUT.output_bps=16;//16bit
OUT.output_color=0;//RAW
//openfile
if((ret = RawProcessor.open_file(szFile))!= LIBRAW_SUCCESS)
{
fprintf(stderr,"Can not open%s:%s\n",szFile,libraw_strerror(ret));
RawProcessor.recycle();
return FALSE;
}
//RAW size
int height=S.raw_height;
int width=S.raw_width;
//image info
memset(LinsnData.imgdata.make,0,64*sizeof(char));
memset(LinsnData.imgdata.model,0,64*sizeof(char));
memcpy(LinsnData.imgdata.make,P1.make,strlen(P1.make));
memcpy(LinsnData.imgdata.model,P1.model,strlen(P1.model));
LinsnData.imgdata.aperture=P2.aperture;
LinsnData.imgdata.iso_speed=P2.iso_speed;
LinsnData.imgdata.shutter=P2.shutter;
char timestamp[64]= {0};
tm* local = localtime(&P2.timestamp); //
strftime(LinsnData.imgdata.timestamp, 64, "%Y-%m-%d %H:%M:%S", local);
/***************************************************************************************************/
//
if((ret = RawProcessor.unpack())!= LIBRAW_SUCCESS)
{
fprintf(stderr,"Can not unpack_thumb%s:%s\n",szFile,libraw_strerror(ret));
//if(LIBRAW_FATAL_ERROR(ret))
goto end;
}
WORD *bmpData=RawProcessor.imgdata.rawdata.raw_image;
int nWidth = width/2;
int nHeight = height/2;
WORD *m_bmpDataR = new WORD[nWidth*nHeight];
WORD *m_bmpDataG = new WORD[nWidth*nHeight];
WORD *m_bmpDataB = new WORD[nWidth*nHeight];
//
for(int i=0;i<nHeight;i++)
{
for(int j=0;j<nWidth;j++)
{
m_bmpDataB[i*nWidth+j] = bmpData[i*nWidth*4+nWidth*2+j*2+1];
m_bmpDataG[i*nWidth+j] = ((bmpData[i*nWidth*4+nWidth*2+j*2]+bmpData[i*nWidth*4+j*2+1])>>1);
m_bmpDataR[i*nWidth+j] = bmpData[i*nWidth*4+j*2];
}
}

creating callbacks and structs for repeated field in a protobuf message in nanopb in c

I have a proto message defined as:
message SimpleMessage {
repeated int32 number = 1;}
now, after compiling, the field is of pb_callback_t and I suppose to write that function. (without .options file)
now, where and what should the function contain? where does the data itself being stored and how can I access it/ assign new data to it?
* EDIT *
according to #Groo 's answer, this is the code I tried:
typedef struct {
int numbers_decoded;
} DecodingState;
bool read_single_number(pb_istream_t *istream, const pb_field_t *field, void **arg)
{
// get the pointer to the custom state
DecodingState *state = (DecodingState*)(*arg);
int32_t value;
if (!pb_decode_varint32(istream, &value))
{
const char * error = PB_GET_ERROR(istream);
printf("Protobuf error: %s", error);
return false;
}
printf("Decoded successfully: %d", value);
state->numbers_decoded++;
return true;
}
int main(void) {
int32_t arr[3] = {10, 22, 342};
uint8_t buffer[128];
size_t message_length;
bool status;
SimpleMessage simple = SimpleMessage_init_zero;
printf("\nbefore : arr[0] = %d\n",arr[0]);
// set the argument and the callback fn
simple.number.arg = &arr;
simple.number.funcs.decode = read_single_number;
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
status = pb_encode(&ostream, SimpleMessage_fields, &simple);
message_length = ostream.bytes_written;
SimpleMessage simple1 = SimpleMessage_init_zero;
simple = simple1;
arr[0] = 0;
pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
// this function will call read_single_number several times
status = pb_decode(&istream, SimpleMessage_fields, &simple);
printf("\nafter : arr[0] = %d\n",arr[0]);
return EXIT_SUCCESS;
}
and the output is:
before : arr[0] = 10
Decoded successfully: 17
after : arr[0] = 0
what do I do wrong?
You can use some nanopb-specific proto flags to force nanopb to generate structs with statically allocated arrays.
However, the default behavior of nanopb's protogen is to generate a callback function which is called by nanopb during encoding (once for the entire list) and decoding (once for each item in the list). This is sometimes preferred in low-memory embedded systems, because you don't need to allocate more than one item at a time.
So, for your .proto file:
message SimpleMessage {
repeated int32 number = 1;
}
You might get something like:
typedef struct _SimpleMessage {
pb_callback_t number;
} SimpleMessage;
Meaning you will have to create your own callback function which will be called for each item in succession.
So for simplicity, let's say you have a simple "variable length" list like this:
#define MAX_NUMBERS 32
typedef struct
{
int32_t numbers[MAX_NUMBERS];
int32_t numbers_count;
}
IntList;
// add a number to the int list
void IntList_add_number(IntList * list, int32_t number)
{
if (list->numbers_count < MAX_NUMBERS)
{
list->numbers[list->numbers_count] = number;
list->numbers_count++;
}
}
Obviously, for such an example, using callbacks wouldn't make any sense, but it makes the example simple.
Encoding callback must iterate through the list, and write the protobuf tag and the value for each item in the list:
bool SimpleMessage_encode_numbers(pb_ostream_t *ostream, const pb_field_t *field, void * const *arg)
{
IntList * source = (IntList*)(*arg);
// encode all numbers
for (int i = 0; i < source->numbers_count; i++)
{
if (!pb_encode_tag_for_field(ostream, field))
{
const char * error = PB_GET_ERROR(ostream);
printf("SimpleMessage_encode_numbers error: %s", error);
return false;
}
if (!pb_encode_svarint(ostream, source->numbers[i]))
{
const char * error = PB_GET_ERROR(ostream);
printf("SimpleMessage_encode_numbers error: %s", error);
return false;
}
}
return true;
}
Decoding callback is called once for each item, and "appends" to the list:
bool SimpleMessage_decode_single_number(pb_istream_t *istream, const pb_field_t *field, void **arg)
{
IntList * dest = (IntList*)(*arg);
// decode single number
int64_t number;
if (!pb_decode_svarint(istream, &number))
{
const char * error = PB_GET_ERROR(istream);
printf("SimpleMessage_decode_single_number error: %s", error);
return false;
}
// add to destination list
IntList_add_number(dest, (int32_t)number);
return true;
}
With these two in place, you must be careful to assign the right callback to the right function:
uint8_t buffer[128];
size_t total_bytes_encoded = 0;
// encoding
{
// prepare the actual "variable" array
IntList actualData = { 0 };
IntList_add_number(&actualData, 123);
IntList_add_number(&actualData, 456);
IntList_add_number(&actualData, 789);
// prepare the nanopb ENCODING callback
SimpleMessage msg = SimpleMessage_init_zero;
msg.number.arg = &actualData;
msg.number.funcs.encode = SimpleMessage_encode_numbers;
// call nanopb
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
if (!pb_encode(&ostream, SimpleMessage_fields, &msg))
{
const char * error = PB_GET_ERROR(&ostream);
printf("pb_encode error: %s", error);
return;
}
total_bytes_encoded = ostream.bytes_written;
printf("Encoded size: %d", total_bytes_encoded);
}
And similar for decoding:
// decoding
{
// empty array for decoding
IntList decodedData = { 0 };
// prepare the nanopb DECODING callback
SimpleMessage msg = SimpleMessage_init_zero;
msg.number.arg = &decodedData;
msg.number.funcs.decode = SimpleMessage_decode_single_number;
// call nanopb
pb_istream_t istream = pb_istream_from_buffer(buffer, total_bytes_encoded);
if (!pb_decode(&istream, SimpleMessage_fields, &msg))
{
const char * error = PB_GET_ERROR(&istream);
printf("pb_decode error: %s", error);
return;
}
printf("Bytes decoded: %d", total_bytes_encoded - istream.bytes_left);
}
If you have a repeated struct inside your message, your callback will not use
nanopb primitive functions (like pb_decode_varint32 above), but again pb_decode for each concrete message type. Your callback can also attach new callbacks to those nested structs, if needed.
To complement Groo's answer, here are answers to your specific questions.
1. Now, where and what should the function contain?
Groo provided good explanation of the callback functions. The network_server example in nanopb repository also uses callbacks and can be a useful reference: network_server/server.c network_server/client.c
2. Where does the data itself being stored?
Wherever you want! The whole point of nanopb's callbacks is that it gives you full flexibility in deciding how to store your data. In some cases you may want to even process the data on the fly, not storing it anywhere.
For example, the network_server example above gets the filenames from filesystem and sends them to the network directly - this way it can handle any amount of files without requiring much memory.
3. How can I access it/ assign new data to it?
Now this is the downside of callbacks - you'll have to implement your own access and allocation functions for whatever storage you use. That's why for the most common cases, either static allocation (with fixed maximum size) or dynamic allocation (which malloc()s required amount of memory) are more convenient.

Linux framebuffer driver and custom file operation

I implemented a framebuffer driver retrieved from a former project in Linux for an embedded system and what I need to do is quite simple: draw a small rectangle where the screen is touched.
In order to do this, I want to use the function xxxfb_fillrect() that you can find in the following driver code (some parts are omitted) :
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include <linux/console.h>
#define PCI_VENDOR_ID_XXX 0x10F610F6
#define PCI_DEVICE_ID_XXX 0x2864C826
#define PCI_CLASS_MASK 0x00FF
#define FB_NAME "MFCC8556_vfb_"
#define FB_MAJOR 29
#define VIDEOMEMSIZE (480*800*3)
#define FBIO_TEST _IO('F', 0x21)
/* Global variable */
/*
* Driver data
*/
//static char *videomemory;
static int fb_count = 3;
static u_long videomemorysize = VIDEOMEMSIZE;
/* array of framebuffer */
static struct fb_info **g_fb_list;
static struct fb_fix_screeninfo fix_default __initdata = {
.id = FB_NAME,
.smem_len = VIDEOMEMSIZE,
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_PSEUDOCOLOR,
.xpanstep = 0,
.ypanstep = 0,
.ywrapstep = 0,
.accel = FB_ACCEL_NONE,
};
static struct fb_var_screeninfo var_default __initdata = {
.xres = 800,
.yres = 480,
.xres_virtual = 800,
.yres_virtual = 480,
.bits_per_pixel = 24,
.red = {0, 8, 0},
.green = {0, 8, 0},
.blue = {0, 8, 0},
.grayscale = 0,
.activate = FB_ACTIVATE_TEST,
.height = -1,
.width = -1,
.pixclock = 30060,
.vmode = FB_VMODE_NONINTERLACED,
};
static int xxxfb_init(void);
static int register_fb(struct fb_info *info);
static int set_screen_base(struct fb_info* info);
static int init_fb_info(struct fb_info *info, struct fb_ops *fbops, unsigned int id_no);
static int alloc_fb_info (struct fb_info **info);
/* ------------ Accelerated Functions --------------------- */
/*
* We provide our own functions if we have hardware acceleration
* or non packed pixel format layouts. If we have no hardware
* acceleration, we can use a generic unaccelerated function. If using
* a pack pixel format just use the functions in cfb_*.c. Each file
* has one of the three different accel functions we support.
*/
/**
* xxxfb_fillrect - REQUIRED function. Can use generic routines if
* non acclerated hardware and packed pixel based.
* Draws a rectangle on the screen.
*
* #info: frame buffer structure that represents a single frame buffer
* #region: The structure representing the rectangular region we
* wish to draw to.
*
* This drawing operation places/removes a retangle on the screen
* depending on the rastering operation with the value of color which
* is in the current color depth format.
*/
void xxxfb_fillrect(struct fb_info *info, const struct fb_fillrect *region)
{
/* Meaning of struct fb_fillrect
*
* #dx: The x and y coordinates of the upper left hand corner of the
* #dy: area we want to draw to.
* #width: How wide the rectangle is we want to draw.
* #height: How tall the rectangle is we want to draw.
* #color: The color to fill in the rectangle with.
* #rop: The raster operation. We can draw the rectangle with a COPY
* of XOR which provides erasing effect.
*/
struct fb_fillrect *tmp_fillrect;
/*ptr=(unsigned long*)info->screen_base;
//fill the screen base ///
for(i=0; i<800*10; i++){
*ptr=0x0000FF00;
ptr++;
}*/
printk(KERN_DEBUG "\nfb_fillrect()");
printk(KERN_DEBUG "\nFix Screen Info.id =%s\n", info->fix.id);
/* printk(KERN_INFO "\nstruct fb_fillrect:\n"
"dx = %d\n"
"dy = %d\n"
"width = %d\n"
"height = %d\n"
"color = 0x%08X\n"
"rop = 0x%X\n___\n"
, region->dx, region->dy, region->width, region->height, region->color, region->rop);
*/
// printk(KERN_INFO "_in fill_rectangle : screen_base = 0x%X\n0x_%02X_%02X_%02X_%02X\n...\n",(unsigned int)info->screen_base, *info->screen_base, *(info->screen_base+1), *(info->screen_base+2),*(info->screen_base+3));
tmp_fillrect = kmalloc(sizeof(struct fb_fillrect), GFP_KERNEL);
*tmp_fillrect = *region;
/*tmp_fillrect->dx=400;
tmp_fillrect->dy=200;
tmp_fillrect->width=100;
tmp_fillrect->height=50;
tmp_fillrect->color=0x0000FF00;
tmp_fillrect->rop=0x0;
*/
//tmp = copy_from_user(tmp_fillrect, region, sizeof(struct fb_fillrect));
printk(KERN_INFO "\nstruct fb_fillrect:\n"
"dx = %d\n"
"dy = %d\n"
"width = %d\n"
"height = %d\n"
"color = 0x%08X\n"
"rop = 0x%X\n___\n"
, tmp_fillrect->dx, tmp_fillrect->dy, tmp_fillrect->width, tmp_fillrect->height, tmp_fillrect->color, tmp_fillrect->rop);
//if (tmp) printk(KERN_ERR "**ERROR: copy_from_user = %d\n", tmp);
cfb_fillrect(info, region);
}
...
int xxxfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg){
int ret = 0;
printk(KERN_INFO "fb_ioctl()");
mutex_lock(&info->lock);
switch(cmd) {
case FBIOGET_VSCREENINFO:
printk(KERN_DEBUG "FBIOGET_VSCREENINFO");
break;
case FBIOGET_FSCREENINFO:
printk(KERN_DEBUG "FBIOGET_FSCREENINFO");
break;
case FBIO_TEST:
printk(KERN_DEBUG "FBIO_TEST");
break;
default:
printk(KERN_DEBUG "ioctl DEFAULT");
break;
}
mutex_unlock(&info->lock);
return ret;
}
/*
* Frame buffer operations
*/
static struct fb_ops xxxfb_ops = {
.owner = THIS_MODULE,
.fb_open = xxxfb_open,
.fb_read = fb_sys_read,
.fb_write = fb_sys_write,
.fb_release = xxxfb_release,
.fb_check_var = xxxfb_check_var,
.fb_set_par = xxxfb_set_par,
//.fb_setcolreg = xxxfb_setcolreg,
.fb_blank = xxxfb_blank,
.fb_pan_display = xxxfb_pan_display,
.fb_fillrect = xxxfb_fillrect, /* Needed !!! */
.fb_copyarea = xxxfb_copyarea, /* Needed !!! */
.fb_imageblit = xxxfb_imageblit, /* Needed !!! */
.fb_cursor = xxxfb_cursor, /* Optional !!! */
.fb_sync = xxxfb_sync,
.fb_ioctl = xxxfb_ioctl,
.fb_mmap = xxxfb_mmap,
};
/* ------------------------------------------------------------------------- */
...
/*
* Modularization
*/
module_init(xxxfb_init);
module_exit(xxxfb_exit);
MODULE_LICENSE("GPL");
But the thing is, I have no idea how to call it. I ended up implementing the drawing of the rectangle manually without using this fb_fillrect().
I know this is not a usual open/rd/wr/... operation, but it is here. How do I use it ? Should I call it with ioctl()? If yes, why is fb_fillrect appearing in the frame buffer operations struct ?
Thank you in advance for your help.
The functions in fb_ops are low level operations not directly exposed to user-space. Instead, there is a fb_fops struct in drivers/video/fbdev/core/fbmem.c that contains the handler functions for the open/read/write/ioctl from user-space (see code below). Some of those handler functions may call subsequently call the fb_ops functions from your driver (see do_fb_ioctl function).
static const struct file_operations fb_fops = {
.owner = THIS_MODULE,
.read = fb_read,
.write = fb_write,
.unlocked_ioctl = fb_ioctl,
...
};
static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct fb_info *info = file_fb_info(file);
if (!info)
return -ENODEV;
return do_fb_ioctl(info, cmd, arg);
}
static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
struct fb_ops *fb;
...
switch (cmd) {
case FBIOGET_VSCREENINFO:
...
case FBIOPUT_VSCREENINFO:
...
case FBIOGET_FSCREENINFO:
...
case FBIOPAN_DISPLAY:
...
default:
if (!lock_fb_info(info))
return -ENODEV;
fb = info->fbops;
if (fb->fb_ioctl)
ret = fb->fb_ioctl(info, cmd, arg);
else
ret = -ENOTTY;
unlock_fb_info(info);
}
return ret;
}
In other cases, fb_ops functions may be used by drivers like fbcon that work on top of fbdev. Here is an example of fb_fillrect being called directly by a framebuffer console driver http://lxr.free-electrons.com/source/drivers/video/console/fbcon_cw.c#L80.
static void cw_clear(struct vc_data *vc, struct fb_info *info, int sy,
int sx, int height, int width)
{
struct fbcon_ops *ops = info->fbcon_par;
struct fb_fillrect region;
...
info->fbops->fb_fillrect(info, &region);
}

Resources