Re: [PATCH v6 3/4] Introduction of HP-BIOSCFG driver [3]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 2023-04-03 15:18:31-0500, Jorge Lopez wrote:
> Hi Thomas,
> 
> 
> >
> > Currently the driver stores all its state in driver-global static data.
> > The kobjects are stored without any state.
> > Inside the kobject attribute operations is some fiddly logic that tries
> > to figure out the corresponding state with a fiddly mechansims.
> >
> > The more correct way would be to attach the corresponding state
> > directly to the kobject.
> >
> > Let me know if you want to give this a shot and I'll give an example.
> 
> Yes.  I would  like to give it a shot.  I can take a look at the code
> and determine when we can implement it.
> No promises.

/* data for each kernel object */
struct bios_property {
	/* This is *not* a pointer, it will be used by the core sysfs
	 * code framework to manage this "object" */
	struct kobject kobj;
	int instance_id; /* instance ID to pass to WMI functions */
	/* common members to all properties */
	u8 display_name[MAX_BUFF];
	u8 path[MAX_BUFF];
	/* all the other common stuff */

	const struct *property_ops ops;
	union {
		struct string_property_data {
			u8 current_value[MAX_BUFF];
			u8 new_value[MAX_BUFF];
			u32 min_length;
			u32 max_length;
		} string_data;
		/* same for other data types... */
	};
};

struct property_ops {
	ssize_t	(*show_current_value)(struct bios_property *, char *);
	ssize_t	(*store_current_value)(struct bios_property *, const char *, size_t);
};

static ssize_t string_property_show_current_value(struct bios_property *prop, char *buf)
{
	/* or read from WMI. Does it need to be cached? */
	return sysfs_emit(buf, prop->string_data.current_value);
}

ssize_t	string_property_store_current_value(struct bios_property *prop, const char *buf, size_t count)
{
	int ret;

	if (strlen(buf) > prop->string_data.max_length)
		return -ERANGE;

	ret = do_string_specifc_wmi_stuff(buf, count);
	if (ret)
		return ret;

	strcpy(prop->current_value, buf);
	return count;
}

/* different show/store functionality per property type */
static const struct property_ops string_property_ops = {
	.store_current_value = string_property_show_current_value,
	.show_current_value = string_property_show_current_value,
};

struct bioscfg_attribute {
	struct attribute attr;
	ssize_t (*show)(struct bioscfg_prop *prop, char *buf);
	ssize_t (*store)(struct bioscfg_prop *prop, const char *buf, size_t count);
};

/* this is one implementation for *all* property types */
static ssize_t display_name_show(struct bioscfg_prop *prop, char *buf)
{
	return sysfs_emit(buf, prop->display_name);
}
static struct bioscfg_attribute display_name = __ATTR_RO(display_name);

/* and all the other ones */

/* this dispatches into the type-specific property handlers */
static ssize_t current_value_show(struct bioscfg_prop *prop, char *buf)
{
	return prop->ops->show_current_value(prop, buf);
}
static struct bioscfg_attribute current_value = __ATTR(current_value);

static struct attribute *attrs[] = {
	&display_name.attr,
	/* other attrs here */
	NULL
};

/* reflect read-only mode in sysfs */
static umode_t bioscfg_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n)
{
	struct bios_property *prop = container_of(kobj, struct bios_property, kobj);

	if (attr == &current_value.attr && prop->read_only)
		return attr->mode ^ 0222; /* clear writable bits */
	return attr->mode;
}

static const struct attribute_group attr_group = {
	.attrs      = attrs,
	.is_visible = bioscfg_attr_is_visible,
};

/* the following two functions dispatch from your the core kobj pointer
 * to your custom callbacks operating on nice bioscfg_attribute
 */
static ssize_t bioscfg_attr_show(struct kobject *kobj, struct attribute *attr,
				 char *buf)
{
	struct bioscfg_attribute *kattr;
	ssize_t ret = -EIO;

	kattr = container_of(attr, struct bioscfg_attribute, attr);
	if (kattr->show)
		ret = kattr->show(kobj, kattr, buf);
	return ret;
}

static ssize_t bioscfg_attr_store(struct kobject *kobj, struct attribute *attr,
				  const char *buf, size_t count)
{
	struct bioscfg_attribute *kattr;
	ssize_t ret = -EIO;

	kattr = container_of(attr, struct bioscfg_attribute, attr);
	if (kattr->store)
		ret = kattr->store(kobj, kattr, buf, count);
	return ret;
}

static const struct sysfs_ops bioscfg_kobj_sysfs_ops = {
	.show	= bioscfg_attr_show,
	.store	= bioscfg_attr_store,
};

/* to hook this into the generic kobject machinery */
static const struct kobj_type bioscfg_kobj_type = {
	.release	= free_struct_bios_property,
	.sysfs_ops	= &bios_property_sysfs_ops,
	.default_groups = attr_groups,
};

static int probe(void)
{
	struct bios_property *prop;

	for (each property discovered via WMI) {
		prop = kzalloc(sizeof(*prop));
		prop->readonly = is_read_only(property);
		/* other common properties */
		if (is_string_property(property)) {
			prop->ops = string_property_ops;
			prop->string_data.current_value = "";
			/* other type-specific properties */
		} else {
			; /* and so on for other types */
		}

		kobject_init(&prop->kobj, &bioscfg_kobj_type);
		kobject_add(&prop->kobj, parent, name);
	}

	/* Now all properties and their memory are managed by the kernel */
}

Instead of having one kobj_type for all properties it would also be
possible to create a new one for each. But I don't think it's worth it.



[Index of Archives]     [Linux Kernel Development]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux