Driver development from scratch, linux driver (25, framebuffer subsystem)

Keywords: Mobile Attribute Linux

I. concept

Framebuffer, also known as frame buffer, whose content corresponds to the interface display on the screen, can be simply understood as the buffer corresponding to the content displayed on the screen, modifying the content in the Framebuffer, that is to say, modifying the content on the screen, so direct operation of the Framebuffer can directly observe the effect from the display.

But Framebuffer is not a direct pixel representation of screen content. Framebuffer actually contains several caches with different functions, such as color caching, depth caching, etc., which are not specified in detail. All you need to know is that these caches work together to form the final image displayed on the screen.

Framebuffer is essentially a segment of memory, or display memory.

Framebuffer is a logical concept. It is not a fixed physical area called Framebuffer on display or memory. In fact, physics is display memory or memory, so long as it is within the space accessible by GPU (the physical address space of GPU), any allocation of a segment of memory (or display memory) can be used as a Framebuffer, only after allocation, the memory area information can be set to the register related to the graphics card. This is actually similar to the concept of a DMA region.

2. Frameebuffer subsystem in linux

1. Relevant data structure

 

The following structure is a collection of all information about fremebuffer devices, including all information about fb devices, including device settings, status and function pointers for underlying hardware operations, etc.

1.fb_info 

include/linux/fb.h
struct fb_info {
	atomic_t count;            /* Open counting */
	int node;                  /* Storing the subscript of the fb in the fb array can also be understood as secondary device information. */
	int flags;                    /* Some logo information */
	struct mutex lock;		/* Lock for open/release/ioctl funcs */
	struct mutex mm_lock;		/* Lock for fb_mmap and smem_* fields */
	struct fb_var_screeninfo var;	/* Current var Variable parameter information */
	struct fb_fix_screeninfo fix;	/* Current fix Fixed parameter information */
	struct fb_monspecs monspecs;	/* Current Monitor specs Display Standard */
	struct work_struct queue;	/* Framebuffer event queue Waiting for queue nodes  */
	struct fb_pixmap pixmap;	/* Image hardware mapper Image Hardware Mapping */
	struct fb_pixmap sprite;	/* Cursor hardware mapper Cursor hardware */
	struct fb_cmap cmap;		/* Current cmap  Current color table*/
	struct list_head modelist;      /* mode list Schema list */
	struct fb_videomode *mode;	/* current mode Current display mode */

#ifdef CONFIG_FB_BACKLIGHT
	/* assigned backlight device */
	/* set before framebuffer registration, 
	   remove after unregister */
	struct backlight_device *bl_dev;   //The corresponding backlight device

	/* Backlight level curve */
	struct mutex bl_curve_mutex;	
	u8 bl_curve[FB_BACKLIGHT_LEVELS];  //Backlight adjustment
#endif
#ifdef CONFIG_FB_DEFERRED_IO
	struct delayed_work deferred_work;
	struct fb_deferred_io *fbdefio;
#endif

	struct fb_ops *fbops;           // Function pointer for operation of underlying hardware devices
	struct device *device;		/* This is the parent Parent device node */
	struct device *dev;		/* This is this fb device  Current Frame Buffer Device */
	int class_flag;                    /* private sysfs flags */
#ifdef CONFIG_FB_TILEBLITTING
	struct fb_tile_ops *tileops;    /* Tile Blitting */
#endif
	char __iomem *screen_base;	/* Virtual address Virtual address */
	unsigned long screen_size;	/* Amount of ioremapped VRAM or 0 LCD IO Mapped virtual memory size  */ 
	void *pseudo_palette;		/* Fake palette of 16 colors Pseudo-16 Colour Table (Adjustment Board)  */ 
#define FBINFO_STATE_RUNNING	0
#define FBINFO_STATE_SUSPENDED	1
	u32 state;			/* Hardware state i.e suspend The state of being suspended or reset. */
	void *fbcon_par;                /* fbcon use-only private area */
	/* From here on everything is device dependent */
	void *par;
	/* we need the PCI or similar aperture base/size not
	   smem_start/size as smem_start may just be an object
	   allocated inside the aperture so may not actually overlap */
	struct apertures_struct {
		unsigned int count;
		struct aperture {
			resource_size_t base;
			resource_size_t size;
		} ranges[0];
	} *apertures;

	bool skip_vt_switch; /* no VT switch on suspend/resume required */
};

2.fb_var_screeninfo 

struct fb_var_screeninfo {
	__u32 xres;			/* visible resolution	How many pixels are there in a row in a visual area	*/
	__u32 yres;                /* How many pixels are there in a column?  */
	__u32 xres_virtual;		/* virtual resolution	Virtual area, how many pixels are there in a row, which simply means that the range defined in memory is relatively large.	*/
	__u32 yres_virtual;        /* Virtual area, how many pixels are there in a column */
	__u32 xoffset;			/* offset from virtual to visible Line offset between virtual and visible screens  */
	__u32 yoffset;			/* resolution	Column offset between virtual and visible screens		*/

	__u32 bits_per_pixel;		/* guess what	bit number per pixel		*/
	__u32 grayscale;		/* 0 = color, 1 = grayscale,Equivalent to zero is black and white (gray level)	*/
					/* >1 = FOURCC			*/
    // The position of red green and blue can be set by pixel per bpp; pixel per BPP can be set by ioctl
	struct fb_bitfield red;		/* bitfield in fb mem if true color, */
	struct fb_bitfield green;	/* else only length is significant */
	struct fb_bitfield blue;
	struct fb_bitfield transp;	/* transparency			*/	

	__u32 nonstd;			/* != 0 Non standard pixel format */

	__u32 activate;			/* see FB_ACTIVATE_*		*/

	__u32 height;			/* height of picture in mm    */
	__u32 width;			/* width of picture in mm     */

	__u32 accel_flags;		/* (OBSOLETE) see fb_info.flags */

	/* Timing: All values in pixclocks, except pixclock (of course)
       Timing, these parts are the display method of the display, and the specific LCD screen related, in the driver is generally placed in the specific LCD screen configuration file. 
     */
	__u32 pixclock;			/* pixel clock in ps (pico seconds)Pixel clock  */
	__u32 left_margin;		/* time from sync to picture Line switching, the delay between synchronization and drawing	*/
	__u32 right_margin;		/* time from picture to sync Line switching, the delay between drawing and synchronization	*/
	__u32 upper_margin;		/* time from sync to picture Frame switching, delay between synchronization and drawing	*/
	__u32 lower_margin;    //Frame switching, the delay between drawing and synchronization
	__u32 hsync_len;		/* length of horizontal sync Length of Horizontal Synchronization	*/
	__u32 vsync_len;		/* length of vertical sync Length of Vertical Synchronization	*/
	__u32 sync;			/* see FB_SYNC_*		*/
	__u32 vmode;			/* see FB_VMODE_*		*/
	__u32 rotate;			/* angle we rotate counter clockwise */
	__u32 colorspace;		/* colorspace for FOURCC-based modes */
	__u32 reserved[4];		/* Reserved for future compatibility */
};

3.fb_fix_screeninfo 

struct fb_fix_screeninfo {
	char id[16];			/* identification string eg "TT Builtin" String-like identifiers  */
	unsigned long smem_start;	/* Start of frame buffer mem fb The start of the cache, here is the physical address */
					/* (physical address) */
	__u32 smem_len;			/* Length of frame buffer mem  fb Cache length */
	__u32 type;			/* see FB_TYPE_*	Look at FB_TYPE_	*/
	__u32 type_aux;			/* Interleave for interleaved Planes Dividing line */
	__u32 visual;			/* see FB_VISUAL_* Look at FB_VISUAL_*		*/ 
	__u16 xpanstep;			/* zero if no hardware panning If there is no hardware panning, assign a value of 0 */
	__u16 ypanstep;			/* zero if no hardware panning If there is no hardware panning, assign a value of 0 */
	__u16 ywrapstep;		/* zero if no hardware ywrap  If there is no hardware ywrap, assign a value of 0   */
	__u32 line_length;		/* length of a line in bytes Number of bytes in a row   */
	unsigned long mmio_start;	/* Start of Memory Mapped I/O Starting position of memory mapping IO  */
					/* (physical address) */
	__u32 mmio_len;			/* Length of Memory Mapped I/O Length of memory mapped IO */
	__u32 accel;			/* Indicate to driver which	*/
					/*  specific chip/card we have Special chips are needed	*/
	__u16 capabilities;		/* see FB_CAP_*			*/
	__u16 reserved[2];		/* Reserved for future compatibility Retain  */
};

4.fb_ops 

/*
 * Frame buffer operations
 *
 * LOCKING NOTE: those functions must _ALL_ be called with the console
 * semaphore held, this is the only suitable locking mechanism we have
 * in 2.6. Some may be called at interrupt time at this point though.
 *
 * The exception to this is the debug related hooks.  Putting the fb
 * into a debug state (e.g. flipping to the kernel console) and restoring
 * it must be done in a lock-free manner, so low level drivers should
 * keep track of the initial console (if applicable) and may need to
 * perform direct, unlocked hardware writes in these hooks.
 */

struct fb_ops {
	/* open/release and usage marking */
	struct module *owner;
	int (*fb_open)(struct fb_info *info, int user);
	int (*fb_release)(struct fb_info *info, int user);

	/* For framebuffers with strange non linear layouts or that do not
	 * work with normal memory mapped access
	 */
	ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
			   size_t count, loff_t *ppos);
	ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,
			    size_t count, loff_t *ppos);

	/* checks var and eventually tweaks it to something supported,
	 * DO NOT MODIFY PAR */
	int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);

	/* set the video mode according to info->var */
	int (*fb_set_par)(struct fb_info *info);

	/* set color register */
	int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
			    unsigned blue, unsigned transp, struct fb_info *info);

	/* set color registers in batch */
	int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);

	/* blank display */
	int (*fb_blank)(int blank, struct fb_info *info);

	/* pan display */
	int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);

	/* Draws a rectangle */
	void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
	/* Copy data from area to another */
	void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
	/* Draws a image to the display */
	void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);

	/* Draws cursor */
	int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);

	/* Rotates the display */
	void (*fb_rotate)(struct fb_info *info, int angle);

	/* wait for blit idle, optional */
	int (*fb_sync)(struct fb_info *info);

	/* perform fb specific ioctl (optional) */
	int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
			unsigned long arg);

	/* Handle 32bit compat ioctl (optional) */
	int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
			unsigned long arg);

	/* perform fb specific mmap */
	int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);

	/* get capability given var */
	void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
			    struct fb_var_screeninfo *var);

	/* teardown any resources to do with this framebuffer */
	void (*fb_destroy)(struct fb_info *info);

	/* called at KDB enter and leave time to prepare the console */
	int (*fb_debug_enter)(struct fb_info *info);
	int (*fb_debug_leave)(struct fb_info *info);
};

5.fb_pixmap

struct fb_pixmap {
	u8  *addr;		/* pointer to memory	Virtual address for mapping		*/
	u32 size;		/* size of buffer in bytes	Size	*/
	u32 offset;		/* current offset to buffer	deviation	*/
	u32 buf_align;		/* byte alignment of each bitmap	*/
	u32 scan_align;		/* alignment per scanline Scanning line to it		*/
	u32 access_align;	/* alignment per read/write (bits)	*/
	u32 flags;		/* see FB_PIXMAP_*			*/
	u32 blit_x;             /* supported bit block dimensions (1-32)*/
	u32 blit_y;             /* Format: blit_x = 1 << (width - 1)    */
	                        /*         blit_y = 1 << (height - 1)   */
	                        /* if 0, will be set to 0xffffffff (all)*/
	/* access methods */
	void (*writeio)(struct fb_info *info, void __iomem *dst, void *src, unsigned int size);
	void (*readio) (struct fb_info *info, void *dst, void __iomem *src, unsigned int size);
};

 

 

2. Frameebuffer Driver Framework

1. Registration of FB subsystem

/**
 *	fbmem_init - init frame buffer subsystem
 *
 *	Initialize the frame buffer subsystem.
 *
 *	NOTE: This function is _only_ to be called by drivers/char/mem.c.
 *
 */

static int __init
fbmem_init(void)
{
    /* Create proc file system operation interface about fb */
	proc_create("fb", 0, NULL, &fb_proc_fops);

    /* Registered Character Device Driver and Operating Interface */
	if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
		printk("unable to get major %d for fb devs\n", FB_MAJOR);
    /* Create image classes */
	fb_class = class_create(THIS_MODULE, "graphics");
	if (IS_ERR(fb_class)) {
		printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
		fb_class = NULL;
	}
	return 0;
}

#ifdef MODULE    
/*  If it is compiled into a module, the module_init is used to create the init.text segment for insmod installation, and the fbmem_exit is added to uninstall the registered proc file system and character device number. */            
module_init(fbmem_init);
static void __exit
fbmem_exit(void)
{
	remove_proc_entry("fb", NULL);
	class_destroy(fb_class);
	unregister_chrdev(FB_MAJOR, "fb");
}

module_exit(fbmem_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Framebuffer base");
#else
/* If compiled into the kernel, the subsystem framework must first drive the execution creation and the execution of the necessary information. */
subsys_initcall(fbmem_init);
#endif

The only explanation is whether the fb device is registered with the old-fashioned unified registration interface register_chrdev. So the secondary device number in it needs to be separated by itself, and the corresponding interfaces of different FBS are found separately.

2. Register an fb device

static DEFINE_MUTEX(registration_lock);    /* static global */


/**
 *	register_framebuffer - registers a frame buffer device
 *	@fb_info: frame buffer info structure
 *
 *	Registers a frame buffer device @fb_info.
 *
 *	Returns negative errno on error, or zero for success.
 *
 */
int
register_framebuffer(struct fb_info *fb_info)
{
	int ret;
    
    /* Lock to ensure that when registering an fb device, another device cannot be registered */
	mutex_lock(&registration_lock);
	ret = do_register_framebuffer(fb_info);
	mutex_unlock(&registration_lock);

	return ret;
}


static int do_register_framebuffer(struct fb_info *fb_info)
{
	int i, ret;
	struct fb_event event;
	struct fb_videomode mode;

    /* Determine whether the byte order of the host and GPU is correct */
	if (fb_check_foreignness(fb_info))
		return -ENOSYS;

    /* Check whether the physical address of the memory of the device to be registered and the physical address of the device to be registered overlap. If the physical address overlaps, registration fails. */
	ret = do_remove_conflicting_framebuffers(fb_info->apertures,
						 fb_info->fix.id,
						 fb_is_primary_device(fb_info));
	if (ret)
		return ret;

    /* Whether full fb devices have been registered, default maximum 32 */
	if (num_registered_fb == FB_MAX)
		return -ENXIO;
    
    /* After checking, we can start registering. */
	num_registered_fb++;            /* First put the number of devices + 1 */
	for (i = 0 ; i < FB_MAX; i++)
		if (!registered_fb[i])        /* Find the unused secondary device number */
			break;
	fb_info->node = i;                /* node Storage sub-equipment number */
	atomic_set(&fb_info->count, 1);    /* Initialization Count */
	mutex_init(&fb_info->lock);        /* Initialize two locks */
	mutex_init(&fb_info->mm_lock);

    /* Create device information. Class has been registered before. The device registered here will be under the fb_class directory of sys. */
	fb_info->dev = device_create(fb_class, fb_info->device,
				     MKDEV(FB_MAJOR, i), NULL, "fb%d", i);     /* Note that the name here is fb0 fb1 fb2... */
	if (IS_ERR(fb_info->dev)) {
		/* Not fatal */
		printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
		fb_info->dev = NULL;
	} else
		fb_init_device(fb_info);        /* Register some attribute s interface show and store in sysfs */

    /* If the driver does not implement pixmap in fb_info, the kernel defaults to the parameter */
	if (fb_info->pixmap.addr == NULL) {
		fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
		if (fb_info->pixmap.addr) {
			fb_info->pixmap.size = FBPIXMAPSIZE;
			fb_info->pixmap.buf_align = 1;
			fb_info->pixmap.scan_align = 1;
			fb_info->pixmap.access_align = 32;
			fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
		}
	}	
	fb_info->pixmap.offset = 0;        /* Default no offset */

	if (!fb_info->pixmap.blit_x)
		fb_info->pixmap.blit_x = ~(u32)0;

	if (!fb_info->pixmap.blit_y)
		fb_info->pixmap.blit_y = ~(u32)0;

    /* If the mode list is not initialized, then this side needs to be initialized (a display screen can display multiple modes, such as different resolutions, etc.) */
	if (!fb_info->modelist.prev || !fb_info->modelist.next)
		INIT_LIST_HEAD(&fb_info->modelist);

	if (fb_info->skip_vt_switch)    /* Is there a VT switch for pause and reply? */
		pm_vt_switch_required(fb_info->dev, false);
	else
		pm_vt_switch_required(fb_info->dev, true);

    /* Initialize mode using the parameters in fb_info-> var */
	fb_var_to_videomode(&mode, &fb_info->var);
	fb_add_videomode(&mode, &fb_info->modelist);    /* Adding the schema to the schema list can also add schemas to the schema list in other ways. */
	registered_fb[i] = fb_info;    /* Register the fb_info to the FB master table and use the device number to get it when you use it. */

	event.info = fb_info;   
	console_lock();     /* Locking console */
	if (!lock_fb_info(fb_info)) {    /* Check whether the fb_ops operation interface exists or not, if not, push it out, and do not execute the following notification */
		console_unlock();
		return -ENODEV;
	}
    
    /* Notify an fb driver is registered */
	fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
	unlock_fb_info(fb_info);
	console_unlock();
	return 0;
}

Regarding the above registration, we will analyze the following points:

1. 32 default registrations for FB devices

/* Definitions of frame buffers						*/

#define FB_MAX			32	/* sufficient for now */

struct fb_info *registered_fb[FB_MAX] __read_mostly;

2. If you want to register multiple fb devices, you must pay attention not to duplicate memory addresses. (Note that this is the physical address)

3. About the attributes attribute interface in sys, let's talk about it briefly, and there may be some examples in the following chapters.

/*  Although the name of this function is the initialization device, it actually creates the relevant interface in sys  */
int fb_init_device(struct fb_info *fb_info)
{
	int i, error = 0;

	dev_set_drvdata(fb_info->dev, fb_info);    /* Private data in dev bound to fb_info */

	fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;    /* Add attribute operation flags to the classes of this device */

	for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
		error = device_create_file(fb_info->dev, &device_attrs[i]);    /* Create property files in sysfs */

		if (error)
			break;
	}

	if (error) {
		while (--i >= 0)
			device_remove_file(fb_info->dev, &device_attrs[i]);
		fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
	}

	return 0;
}


/* When cmap is added back in it should be a binary attribute
 * not a text one. Consideration should also be given to converting
 * fbdev to use configfs instead of sysfs */
static struct device_attribute device_attrs[] = {    /* Here are the attributes of all course operations */
	__ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),    /* Let's take this as an example. */
	__ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
	__ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
	__ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
	__ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
	__ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
	__ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
	__ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),
	__ATTR(name, S_IRUGO, show_name, NULL),
	__ATTR(stride, S_IRUGO, show_stride, NULL),
	__ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
	__ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
#ifdef CONFIG_FB_BACKLIGHT
	__ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
#endif
};

Let's first look at a normal device attribute format

/* interface for exporting device attributes */
struct device_attribute {
	struct attribute	attr;        /* Universal, all sys readable and writable needs  */
	ssize_t (*show)(struct device *dev, struct device_attribute *attr,
			char *buf);             /* Interface for displaying kernel space */
	ssize_t (*store)(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t count);    /* User Space Writes to Kernel Space */
};

struct attribute {
	const char		*name;    /* Name */
	umode_t			mode;     /* Jurisdiction */
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	bool			ignore_lockdep:1;
	struct lock_class_key	*key;
	struct lock_class_key	skey;
#endif
};

Let's take a look at the meaning of the operation.

	__ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp), 

#define __ATTR(_name, _mode, _show, _store) {				\
	.attr = {.name = __stringify(_name),				\
		 .mode = VERIFY_OCTAL_PERMISSIONS(_mode) },		\
	.show	= _show,						\
	.store	= _store,						\
}

//Substitute macros with


{
    .sttr = {.name = "bits_per_pixel",
        .mode = VERIFY_OCTAL_PERMISSIONS(S_IRUGO|S_IWUSR) },    /* Everyone can read and the owner can write. */
    .show = show_bpp,
    stroe = store_bpp,
    }
}

//Where u stringify is a conversion string
#define __stringify_1(x...)	#x
#define __stringify(x...)	__stringify_1(x)

VERIFY_OCTAL_PERMISSIONS It's for permission conversion.
/* Permissions on a sysfs file: you didn't miss the 0 prefix did you? */
#define VERIFY_OCTAL_PERMISSIONS(perms)					\
	(BUILD_BUG_ON_ZERO((perms) < 0) +				\
	 BUILD_BUG_ON_ZERO((perms) > 0777) +				\
	 /* User perms >= group perms >= other perms */			\
	 BUILD_BUG_ON_ZERO(((perms) >> 6) < (((perms) >> 3) & 7)) +	\
	 BUILD_BUG_ON_ZERO((((perms) >> 3) & 7) < ((perms) & 7)) +	\
	 (perms))



#define S_IRWXUGO	(S_IRWXU|S_IRWXG|S_IRWXO)
#define S_IALLUGO	(S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)
#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH) /* Everyone has readable rights */
#define S_IWUGO		(S_IWUSR|S_IWGRP|S_IWOTH)
#define S_IXUGO		(S_IXUSR|S_IXGRP|S_IXOTH)


S_IRUSR: User Read 00400
S_IRGRP: User Group Read 00040
S_IROTH: Other readings 00004


#define S_IRWXU 00700
#define S_IRUSR 00400
#define S_IWUSR 00200 Owner Writable
#define S_IXUSR 00100

#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010

#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001

The show function is very simple. It formats and prints the current bpp parameters into the buf array.

static ssize_t show_bpp(struct device *device, struct device_attribute *attr,
			char *buf)
{
	struct fb_info *fb_info = dev_get_drvdata(device);    /* Recall that we put fb_info in device's private data */
	return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->var.bits_per_pixel);
}

Strw is the bpp value of user space changing kernel space

static ssize_t store_bpp(struct device *device, struct device_attribute *attr,
			 const char *buf, size_t count)
{
	struct fb_info *fb_info = dev_get_drvdata(device);    /* Recall that we put fb_info in device's private data */
	struct fb_var_screeninfo var;
	char ** last = NULL;
	int err;

	var = fb_info->var;
	var.bits_per_pixel = simple_strtoul(buf, last, 0);    /* Converting strings to integers */
	if ((err = activate(fb_info, &var)))    /* bpp for writing drive space */
		return err;
	return count;
}

static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var)
{
	int err;

	var->activate |= FB_ACTIVATE_FORCE;
	console_lock();
	fb_info->flags |= FBINFO_MISC_USEREVENT;
	err = fb_set_var(fb_info, var);        /* Setting up the driving space of bpp */
	fb_info->flags &= ~FBINFO_MISC_USEREVENT;
	console_unlock();
	if (err)
		return err;
	return 0;
}

Driver debugging can be done easily by using attribute operation.

 

 

3. Log off a driver

/**
 *	unregister_framebuffer - releases a frame buffer device
 *	@fb_info: frame buffer info structure
 *
 *	Unregisters a frame buffer device @fb_info.
 *
 *	Returns negative errno on error, or zero for success.
 *
 *      This function will also notify the framebuffer console
 *      to release the driver.
 *
 *      This is meant to be called within a driver's module_exit()
 *      function. If this is called outside module_exit(), ensure
 *      that the driver implements fb_open() and fb_release() to
 *      check that no processes are using the device.
 */
int
unregister_framebuffer(struct fb_info *fb_info)
{
	int ret;

	mutex_lock(&registration_lock);
	ret = do_unregister_framebuffer(fb_info);
	mutex_unlock(&registration_lock);

	return ret;
}



static int do_unregister_framebuffer(struct fb_info *fb_info)
{
	struct fb_event event;
	int i, ret = 0;

	i = fb_info->node;    /* Get the sub-equipment number */
    
    /* Check whether the equipment information is correct */
	if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info)
		return -EINVAL;

	console_lock();
	if (!lock_fb_info(fb_info)) {    
		console_unlock();    
		return -ENODEV;
	}

	event.info = fb_info;
	ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event);    /* Notify an uninstall event, otherwise you can't use this */
	unlock_fb_info(fb_info);
	console_unlock();

	if (ret)
		return -EINVAL;

	pm_vt_switch_unregister(fb_info->dev);    /* Unloading hang pause release function */

	unlink_framebuffer(fb_info);
	if (fb_info->pixmap.addr &&        
	    (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))    /* If the default is used, it is register memory, which is released here. */
		kfree(fb_info->pixmap.addr);        
	fb_destroy_modelist(&fb_info->modelist);    /* Delete all patterns from the release mode list */
	registered_fb[i] = NULL;        /* Unload here to clear the global fb table, later registration can be used */
	num_registered_fb--;            /* Total quantity minus 1 */
	fb_cleanup_device(fb_info);    /* Delete Attribute Operating Interface */
	event.info = fb_info;
	console_lock();
	fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);    /* Notification unloading */
	console_unlock();

	/* this may free fb info */
	put_fb_info(fb_info);
	return 0;
}

 

Posted by iconicCreator on Thu, 31 Jan 2019 00:12:16 -0800