brintos

brintos / linux-shallow public Read only

0
0
Text · 12.1 KiB · 1b3c995 Raw
503 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * fbsysfs.c - framebuffer device class and attributes4 *5 * Copyright (c) 2004 James Simmons <jsimmons@infradead.org>6 */7 8#include <linux/console.h>9#include <linux/fb.h>10#include <linux/fbcon.h>11#include <linux/major.h>12 13#include "fb_internal.h"14 15#define FB_SYSFS_FLAG_ATTR 116 17static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var)18{19	int err;20 21	var->activate |= FB_ACTIVATE_FORCE;22	console_lock();23	lock_fb_info(fb_info);24	err = fb_set_var(fb_info, var);25	if (!err)26		fbcon_update_vcs(fb_info, var->activate & FB_ACTIVATE_ALL);27	unlock_fb_info(fb_info);28	console_unlock();29	if (err)30		return err;31	return 0;32}33 34static int mode_string(char *buf, unsigned int offset,35		       const struct fb_videomode *mode)36{37	char m = 'U';38	char v = 'p';39 40	if (mode->flag & FB_MODE_IS_DETAILED)41		m = 'D';42	if (mode->flag & FB_MODE_IS_VESA)43		m = 'V';44	if (mode->flag & FB_MODE_IS_STANDARD)45		m = 'S';46 47	if (mode->vmode & FB_VMODE_INTERLACED)48		v = 'i';49	if (mode->vmode & FB_VMODE_DOUBLE)50		v = 'd';51 52	return snprintf(&buf[offset], PAGE_SIZE - offset, "%c:%dx%d%c-%d\n",53	                m, mode->xres, mode->yres, v, mode->refresh);54}55 56static ssize_t store_mode(struct device *device, struct device_attribute *attr,57			  const char *buf, size_t count)58{59	struct fb_info *fb_info = dev_get_drvdata(device);60	char mstr[100];61	struct fb_var_screeninfo var;62	struct fb_modelist *modelist;63	struct fb_videomode *mode;64	size_t i;65	int err;66 67	memset(&var, 0, sizeof(var));68 69	list_for_each_entry(modelist, &fb_info->modelist, list) {70		mode = &modelist->mode;71		i = mode_string(mstr, 0, mode);72		if (strncmp(mstr, buf, max(count, i)) == 0) {73 74			var = fb_info->var;75			fb_videomode_to_var(&var, mode);76			if ((err = activate(fb_info, &var)))77				return err;78			fb_info->mode = mode;79			return count;80		}81	}82	return -EINVAL;83}84 85static ssize_t show_mode(struct device *device, struct device_attribute *attr,86			 char *buf)87{88	struct fb_info *fb_info = dev_get_drvdata(device);89 90	if (!fb_info->mode)91		return 0;92 93	return mode_string(buf, 0, fb_info->mode);94}95 96static ssize_t store_modes(struct device *device,97			   struct device_attribute *attr,98			   const char *buf, size_t count)99{100	struct fb_info *fb_info = dev_get_drvdata(device);101	LIST_HEAD(old_list);102	int i = count / sizeof(struct fb_videomode);103 104	if (i * sizeof(struct fb_videomode) != count)105		return -EINVAL;106 107	console_lock();108	lock_fb_info(fb_info);109 110	list_splice(&fb_info->modelist, &old_list);111	fb_videomode_to_modelist((const struct fb_videomode *)buf, i,112				 &fb_info->modelist);113	if (fb_new_modelist(fb_info)) {114		fb_destroy_modelist(&fb_info->modelist);115		list_splice(&old_list, &fb_info->modelist);116	} else117		fb_destroy_modelist(&old_list);118 119	unlock_fb_info(fb_info);120	console_unlock();121 122	return 0;123}124 125static ssize_t show_modes(struct device *device, struct device_attribute *attr,126			  char *buf)127{128	struct fb_info *fb_info = dev_get_drvdata(device);129	unsigned int i;130	struct fb_modelist *modelist;131	const struct fb_videomode *mode;132 133	i = 0;134	list_for_each_entry(modelist, &fb_info->modelist, list) {135		mode = &modelist->mode;136		i += mode_string(buf, i, mode);137	}138	return i;139}140 141static ssize_t store_bpp(struct device *device, struct device_attribute *attr,142			 const char *buf, size_t count)143{144	struct fb_info *fb_info = dev_get_drvdata(device);145	struct fb_var_screeninfo var;146	char ** last = NULL;147	int err;148 149	var = fb_info->var;150	var.bits_per_pixel = simple_strtoul(buf, last, 0);151	if ((err = activate(fb_info, &var)))152		return err;153	return count;154}155 156static ssize_t show_bpp(struct device *device, struct device_attribute *attr,157			char *buf)158{159	struct fb_info *fb_info = dev_get_drvdata(device);160	return sysfs_emit(buf, "%d\n", fb_info->var.bits_per_pixel);161}162 163static ssize_t store_rotate(struct device *device,164			    struct device_attribute *attr,165			    const char *buf, size_t count)166{167	struct fb_info *fb_info = dev_get_drvdata(device);168	struct fb_var_screeninfo var;169	char **last = NULL;170	int err;171 172	var = fb_info->var;173	var.rotate = simple_strtoul(buf, last, 0);174 175	if ((err = activate(fb_info, &var)))176		return err;177 178	return count;179}180 181 182static ssize_t show_rotate(struct device *device,183			   struct device_attribute *attr, char *buf)184{185	struct fb_info *fb_info = dev_get_drvdata(device);186 187	return sysfs_emit(buf, "%d\n", fb_info->var.rotate);188}189 190static ssize_t store_virtual(struct device *device,191			     struct device_attribute *attr,192			     const char *buf, size_t count)193{194	struct fb_info *fb_info = dev_get_drvdata(device);195	struct fb_var_screeninfo var;196	char *last = NULL;197	int err;198 199	var = fb_info->var;200	var.xres_virtual = simple_strtoul(buf, &last, 0);201	last++;202	if (last - buf >= count)203		return -EINVAL;204	var.yres_virtual = simple_strtoul(last, &last, 0);205 206	if ((err = activate(fb_info, &var)))207		return err;208	return count;209}210 211static ssize_t show_virtual(struct device *device,212			    struct device_attribute *attr, char *buf)213{214	struct fb_info *fb_info = dev_get_drvdata(device);215	return sysfs_emit(buf, "%d,%d\n", fb_info->var.xres_virtual,216			fb_info->var.yres_virtual);217}218 219static ssize_t show_stride(struct device *device,220			   struct device_attribute *attr, char *buf)221{222	struct fb_info *fb_info = dev_get_drvdata(device);223	return sysfs_emit(buf, "%d\n", fb_info->fix.line_length);224}225 226static ssize_t store_blank(struct device *device,227			   struct device_attribute *attr,228			   const char *buf, size_t count)229{230	struct fb_info *fb_info = dev_get_drvdata(device);231	char *last = NULL;232	int err, arg;233 234	arg = simple_strtoul(buf, &last, 0);235	console_lock();236	err = fb_blank(fb_info, arg);237	/* might again call into fb_blank */238	fbcon_fb_blanked(fb_info, arg);239	console_unlock();240	if (err < 0)241		return err;242	return count;243}244 245static ssize_t show_blank(struct device *device,246			  struct device_attribute *attr, char *buf)247{248//	struct fb_info *fb_info = dev_get_drvdata(device);249	return 0;250}251 252static ssize_t store_console(struct device *device,253			     struct device_attribute *attr,254			     const char *buf, size_t count)255{256//	struct fb_info *fb_info = dev_get_drvdata(device);257	return 0;258}259 260static ssize_t show_console(struct device *device,261			    struct device_attribute *attr, char *buf)262{263//	struct fb_info *fb_info = dev_get_drvdata(device);264	return 0;265}266 267static ssize_t store_cursor(struct device *device,268			    struct device_attribute *attr,269			    const char *buf, size_t count)270{271//	struct fb_info *fb_info = dev_get_drvdata(device);272	return 0;273}274 275static ssize_t show_cursor(struct device *device,276			   struct device_attribute *attr, char *buf)277{278//	struct fb_info *fb_info = dev_get_drvdata(device);279	return 0;280}281 282static ssize_t store_pan(struct device *device,283			 struct device_attribute *attr,284			 const char *buf, size_t count)285{286	struct fb_info *fb_info = dev_get_drvdata(device);287	struct fb_var_screeninfo var;288	char *last = NULL;289	int err;290 291	var = fb_info->var;292	var.xoffset = simple_strtoul(buf, &last, 0);293	last++;294	if (last - buf >= count)295		return -EINVAL;296	var.yoffset = simple_strtoul(last, &last, 0);297 298	console_lock();299	err = fb_pan_display(fb_info, &var);300	console_unlock();301 302	if (err < 0)303		return err;304	return count;305}306 307static ssize_t show_pan(struct device *device,308			struct device_attribute *attr, char *buf)309{310	struct fb_info *fb_info = dev_get_drvdata(device);311	return sysfs_emit(buf, "%d,%d\n", fb_info->var.xoffset,312			fb_info->var.yoffset);313}314 315static ssize_t show_name(struct device *device,316			 struct device_attribute *attr, char *buf)317{318	struct fb_info *fb_info = dev_get_drvdata(device);319 320	return sysfs_emit(buf, "%s\n", fb_info->fix.id);321}322 323static ssize_t store_fbstate(struct device *device,324			     struct device_attribute *attr,325			     const char *buf, size_t count)326{327	struct fb_info *fb_info = dev_get_drvdata(device);328	u32 state;329	char *last = NULL;330 331	state = simple_strtoul(buf, &last, 0);332 333	console_lock();334	lock_fb_info(fb_info);335 336	fb_set_suspend(fb_info, (int)state);337 338	unlock_fb_info(fb_info);339	console_unlock();340 341	return count;342}343 344static ssize_t show_fbstate(struct device *device,345			    struct device_attribute *attr, char *buf)346{347	struct fb_info *fb_info = dev_get_drvdata(device);348	return sysfs_emit(buf, "%d\n", fb_info->state);349}350 351#if IS_ENABLED(CONFIG_FB_BACKLIGHT)352static ssize_t store_bl_curve(struct device *device,353			      struct device_attribute *attr,354			      const char *buf, size_t count)355{356	struct fb_info *fb_info = dev_get_drvdata(device);357	u8 tmp_curve[FB_BACKLIGHT_LEVELS];358	unsigned int i;359 360	/* Some drivers don't use framebuffer_alloc(), but those also361	 * don't have backlights.362	 */363	if (!fb_info || !fb_info->bl_dev)364		return -ENODEV;365 366	if (count != (FB_BACKLIGHT_LEVELS / 8 * 24))367		return -EINVAL;368 369	for (i = 0; i < (FB_BACKLIGHT_LEVELS / 8); ++i)370		if (sscanf(&buf[i * 24],371			"%2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx\n",372			&tmp_curve[i * 8 + 0],373			&tmp_curve[i * 8 + 1],374			&tmp_curve[i * 8 + 2],375			&tmp_curve[i * 8 + 3],376			&tmp_curve[i * 8 + 4],377			&tmp_curve[i * 8 + 5],378			&tmp_curve[i * 8 + 6],379			&tmp_curve[i * 8 + 7]) != 8)380			return -EINVAL;381 382	/* If there has been an error in the input data, we won't383	 * reach this loop.384	 */385	mutex_lock(&fb_info->bl_curve_mutex);386	for (i = 0; i < FB_BACKLIGHT_LEVELS; ++i)387		fb_info->bl_curve[i] = tmp_curve[i];388	mutex_unlock(&fb_info->bl_curve_mutex);389 390	return count;391}392 393static ssize_t show_bl_curve(struct device *device,394			     struct device_attribute *attr, char *buf)395{396	struct fb_info *fb_info = dev_get_drvdata(device);397	ssize_t len = 0;398	unsigned int i;399 400	/* Some drivers don't use framebuffer_alloc(), but those also401	 * don't have backlights.402	 */403	if (!fb_info || !fb_info->bl_dev)404		return -ENODEV;405 406	mutex_lock(&fb_info->bl_curve_mutex);407	for (i = 0; i < FB_BACKLIGHT_LEVELS; i += 8)408		len += scnprintf(&buf[len], PAGE_SIZE - len, "%8ph\n",409				fb_info->bl_curve + i);410	mutex_unlock(&fb_info->bl_curve_mutex);411 412	return len;413}414#endif415 416/* When cmap is added back in it should be a binary attribute417 * not a text one. Consideration should also be given to converting418 * fbdev to use configfs instead of sysfs */419static struct device_attribute device_attrs[] = {420	__ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),421	__ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),422	__ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),423	__ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),424	__ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),425	__ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),426	__ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),427	__ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),428	__ATTR(name, S_IRUGO, show_name, NULL),429	__ATTR(stride, S_IRUGO, show_stride, NULL),430	__ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),431	__ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),432#if IS_ENABLED(CONFIG_FB_BACKLIGHT)433	__ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),434#endif435};436 437static int fb_init_device(struct fb_info *fb_info)438{439	int i, error = 0;440 441	dev_set_drvdata(fb_info->dev, fb_info);442 443	fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;444 445	for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {446		error = device_create_file(fb_info->dev, &device_attrs[i]);447 448		if (error)449			break;450	}451 452	if (error) {453		while (--i >= 0)454			device_remove_file(fb_info->dev, &device_attrs[i]);455		fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;456	}457 458	return 0;459}460 461static void fb_cleanup_device(struct fb_info *fb_info)462{463	unsigned int i;464 465	if (fb_info->class_flag & FB_SYSFS_FLAG_ATTR) {466		for (i = 0; i < ARRAY_SIZE(device_attrs); i++)467			device_remove_file(fb_info->dev, &device_attrs[i]);468 469		fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;470	}471}472 473int fb_device_create(struct fb_info *fb_info)474{475	int node = fb_info->node;476	dev_t devt = MKDEV(FB_MAJOR, node);477	int ret;478 479	fb_info->dev = device_create(fb_class, fb_info->device, devt, NULL, "fb%d", node);480	if (IS_ERR(fb_info->dev)) {481		/* Not fatal */482		ret = PTR_ERR(fb_info->dev);483		pr_warn("Unable to create device for framebuffer %d; error %d\n", node, ret);484		fb_info->dev = NULL;485	} else {486		fb_init_device(fb_info);487	}488 489	return 0;490}491 492void fb_device_destroy(struct fb_info *fb_info)493{494	dev_t devt = MKDEV(FB_MAJOR, fb_info->node);495 496	if (!fb_info->dev)497		return;498 499	fb_cleanup_device(fb_info);500	device_destroy(fb_class, devt);501	fb_info->dev = NULL;502}503