From: Bartosz Golaszewski <redacted>
This series adds a new GPIO testing module based on configfs committable items
and sysfs. The goal is to provide a testing driver that will be configurable
at runtime (won't need module reload) and easily extensible. The control over
the attributes is also much more fine-grained than in gpio-mockup.
This series also contains a respin of the patches I sent separately to the
configfs maintainers - these patches implement the concept of committable
items that was well defined for a long time but never actually completed.
Apart from the new driver itself, its selftests and the configfs patches, this
series contains some changes to the bitmap API - most importantly: it adds
devres managed variants of bitmap_alloc() and bitmap_zalloc().
v1 -> v2:
- add selftests for gpio-sim
- add helper programs for selftests
- update the configfs rename callback to work with the new API introduced in
v5.11
- fix a missing quote in the documentation
- use !! whenever using bits operation that are required to return 0 or 1
- use provided bitmap API instead of reimplementing copy or fill operations
- fix a deadlock in gpio_sim_direction_output()
- add new read-only configfs attributes for mapping of configfs items to GPIO
device names
- and address other minor issues pointed out in reviews of v1
Bartosz Golaszewski (12):
configfs: increase the item name length
configfs: use (1UL << bit) for internal flags
configfs: implement committable items
samples: configfs: add a committable group
lib: bitmap: remove the 'extern' keyword from function declarations
lib: bitmap: order includes alphabetically
lib: bitmap: provide devm_bitmap_alloc() and devm_bitmap_zalloc()
drivers: export device_is_bound()
gpio: sim: new testing module
selftests: gpio: provide a helper for reading chip info
selftests: gpio: add a helper for reading GPIO line names
selftests: gpio: add test cases for gpio-sim
Documentation/admin-guide/gpio/gpio-sim.rst | 72 ++
Documentation/filesystems/configfs.rst | 6 +-
drivers/base/dd.c | 1 +
drivers/gpio/Kconfig | 8 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-sim.c | 878 ++++++++++++++++++
fs/configfs/configfs_internal.h | 22 +-
fs/configfs/dir.c | 245 ++++-
include/linux/bitmap.h | 129 +--
include/linux/configfs.h | 3 +-
lib/bitmap.c | 42 +-
samples/configfs/configfs_sample.c | 153 +++
tools/testing/selftests/gpio/.gitignore | 2 +
tools/testing/selftests/gpio/Makefile | 4 +-
tools/testing/selftests/gpio/config | 1 +
tools/testing/selftests/gpio/gpio-chip-info.c | 57 ++
tools/testing/selftests/gpio/gpio-line-name.c | 55 ++
tools/testing/selftests/gpio/gpio-sim.sh | 229 +++++
18 files changed, 1822 insertions(+), 86 deletions(-)
create mode 100644 Documentation/admin-guide/gpio/gpio-sim.rst
create mode 100644 drivers/gpio/gpio-sim.c
create mode 100644 tools/testing/selftests/gpio/gpio-chip-info.c
create mode 100644 tools/testing/selftests/gpio/gpio-line-name.c
create mode 100755 tools/testing/selftests/gpio/gpio-sim.sh
--
2.29.1
From: Bartosz Golaszewski <redacted>
20 characters limit for item name is relatively small. Let's increase it
to 32 to fit '04-committable-children' - a name we'll use in the sample
code for committable items.
Signed-off-by: Bartosz Golaszewski <redacted>
Acked-by: Linus Walleij <redacted>
---
include/linux/configfs.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Bartosz Golaszewski <redacted>
Add an example of using committable items to configfs samples. Each
config item has two attributes: read-write 'storeme' which works
similarly to other examples in this file and a read-only 'committed'
attribute which changes its value between false and true depending on
whether it's committed or not at the moment.
Signed-off-by: Bartosz Golaszewski <redacted>
Acked-by: Linus Walleij <redacted>
---
samples/configfs/configfs_sample.c | 153 +++++++++++++++++++++++++++++
1 file changed, 153 insertions(+)
@@ -315,6 +315,158 @@ static struct configfs_subsystem group_children_subsys = {/* ----------------------------------------------------------------- */+/*+*04-committable-children+*+*Thisisanexampleofacommittablegroup.It'ssimilartothesimple+*childrenexamplebuteachconfig_itemhasanadditional'committed'+*attributewhichisread-onlyandisonlymodifiedwhentheconfig_item+*ismovedfromthe'pending'tothe'live'directory.+*/++structcommittable_child{+structconfig_itemitem;+intstoreme;+boolcommitted;+};++staticinlinestructcommittable_child*+to_committable_child(structconfig_item*item)+{+returncontainer_of(item,structcommittable_child,item);+}++staticssize_t+committable_child_storeme_show(structconfig_item*item,char*page)+{+returnsprintf(page,"%d\n",to_committable_child(item)->storeme);+}++staticssize_tcommittable_child_storeme_store(structconfig_item*item,+constchar*page,size_tcount)+{+structcommittable_child*child=to_committable_child(item);+intret;++if(child->committed)+return-EPERM;++ret=kstrtoint(page,10,&child->storeme);+if(ret)+returnret;++returncount;+}++CONFIGFS_ATTR(committable_child_,storeme);++staticssize_t+committable_child_committed_show(structconfig_item*item,char*page)+{+returnsprintf(page,"%s\n",+to_committable_child(item)->committed?"true":"false");+}++CONFIGFS_ATTR_RO(committable_child_,committed);++staticstructconfigfs_attribute*committable_child_attrs[]={+&committable_child_attr_storeme,+&committable_child_attr_committed,+NULL,+};++staticvoidcommittable_child_release(structconfig_item*item)+{+kfree(to_committable_child(item));+}++staticstructconfigfs_item_operationscommittable_child_item_ops={+.release=committable_child_release,+};++staticconststructconfig_item_typecommittable_child_type={+.ct_item_ops=&committable_child_item_ops,+.ct_attrs=committable_child_attrs,+.ct_owner=THIS_MODULE,+};++structcommittable_children{+structconfig_groupgroup;+};++staticstructconfig_item*+committable_children_make_item(structconfig_group*group,constchar*name)+{+structcommittable_child*child;++child=kzalloc(sizeof(*child),GFP_KERNEL);+if(!child)+returnERR_PTR(-ENOMEM);++config_item_init_type_name(&child->item,name,&committable_child_type);++return&child->item;+}++staticssize_t+committable_children_description_show(structconfig_item*item,char*page)+{+returnsprintf(page,+"[04-committable-children]\n"+"\n"+"This subsystem allows creation of committable config_items. The subsystem\n"+"has two subdirectories: pending and live. New config_items can only be\n"+"created in pending/ and they have one writable and readable attribute as\n"+"well as a single read-only attribute. The latter is only changed once the\n"+"item is 'committed'. This is done by moving the config_item (using\n"+"rename()) to the live/ directory. In this example, the storeme attribute\n"+"becomes 'read-only' once committed.\n");+}++CONFIGFS_ATTR_RO(committable_children_,description);++staticstructconfigfs_attribute*committable_children_attrs[]={+&committable_children_attr_description,+NULL,+};++staticintcommittable_children_commit_item(structconfig_item*item)+{+to_committable_child(item)->committed=true;++return0;+}++staticintcommittable_children_uncommit_item(structconfig_item*item)+{+to_committable_child(item)->committed=false;++return0;+}++staticstructconfigfs_group_operationscommittable_children_group_ops={+.make_item=committable_children_make_item,+.commit_item=committable_children_commit_item,+.uncommit_item=committable_children_uncommit_item,+};++staticconststructconfig_item_typecommittable_children_type={+.ct_group_ops=&committable_children_group_ops,+.ct_attrs=committable_children_attrs,+.ct_owner=THIS_MODULE,+};++staticstructconfigfs_subsystemcommittable_children_subsys={+.su_group={+.cg_item={+.ci_namebuf="04-committable-children",+.ci_type=&committable_children_type,+},+},+};++/* ----------------------------------------------------------------- */+/**We'renowdonewithoursubsystemdefinitions.*Forconvenienceinthismodule,here'salistofthemall.It
From: Bartosz Golaszewski <redacted>
This implements configfs committable items. We mostly follow the
documentation except that we extend config_group_ops with uncommit_item()
callback for reverting the changes made by commit_item().
Each committable group has two sub-directories: pending and live. New
items can only be created in pending/. Attributes can only be modified
while the item is in pending/. Once it's ready to be committed, it must
be moved over to live/ using the rename() system call. This is when the
commit_item() function will be called.
Implementation-wise: we reuse the default group mechanism to elegantly
plug the new pseude-groups into configfs. The pending group inherits the
parent group's operations so that config_items can be seamlesly created
in it using the callbacks supplied by the user as part of the committable
group itself.
Signed-off-by: Bartosz Golaszewski <redacted>
Acked-by: Linus Walleij <redacted>
---
Documentation/filesystems/configfs.rst | 6 +-
fs/configfs/configfs_internal.h | 2 +
fs/configfs/dir.c | 245 ++++++++++++++++++++++++-
include/linux/configfs.h | 1 +
4 files changed, 245 insertions(+), 9 deletions(-)
@@ -490,9 +491,6 @@ pass up an error. Committable Items =================-Note:- Committable items are currently unimplemented.- Some config_items cannot have a valid initial state. That is, no default values can be specified for the item's attributes such that the item can do its work. Userspace must configure one or more attributes,
@@ -532,4 +530,4 @@ method returns zero and the item is moved to the "live" directory. As rmdir(2) does not work in the "live" directory, an item must be shutdown, or "uncommitted". Again, this is done via rename(2), this time from the "live" directory back to the "pending" one. The subsystem-is notified by the ct_group_ops->uncommit_object() method.+is notified by the ct_group_ops->uncommit_item() method.
@@ -1476,6 +1617,12 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)return-EINVAL;}+parent_sd=dentry->d_parent->d_fsdata;+if(parent_sd->s_type&CONFIGFS_GROUP_LIVE){+config_item_put(parent_item);+return-EPERM;+}+/* configfs_mkdir() shouldn't have allowed this */BUG_ON(!subsys->su_group.cg_item.ci_type);subsys_owner=subsys->su_group.cg_item.ci_type->ct_owner;
@@ -1562,9 +1709,97 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)return0;}+staticintconfigfs_rename(structuser_namespace*mnt_userns,+structinode*old_dir,structdentry*old_dentry,+structinode*new_dir,structdentry*new_dentry,+unsignedintflags)+{+structconfigfs_dirent*sd,*old_parent_sd,*new_parent_sd;+structdentry*old_parent_dentry,*new_parent_dentry;+structdentry*committable_group_dentry;+structconfig_item*committable_group_item,*item,*new_parent_item;+structconfigfs_subsystem*committable_group_subsys;+structconfigfs_group_operations*committable_group_ops;+intret=0;++if(flags)+return-EINVAL;++old_parent_dentry=old_dentry->d_parent;+new_parent_dentry=new_dentry->d_parent;++sd=old_dentry->d_fsdata;+old_parent_sd=old_dentry->d_parent->d_fsdata;+new_parent_sd=new_dentry->d_parent->d_fsdata;++if(!old_parent_sd||!new_parent_sd)+return-EPERM;++/*+*Renamingmustalwaysbebetweena'pending'anda'live'groupand+*bothneedtohavethesameparent.Changingthedirectorynameis+*notallowed.+*/+if(!((old_parent_sd->s_type&CONFIGFS_GROUP_PENDING)&&+(new_parent_sd->s_type&CONFIGFS_GROUP_LIVE))&&+!((old_parent_sd->s_type&CONFIGFS_GROUP_LIVE)&&+(new_parent_sd->s_type&CONFIGFS_GROUP_PENDING)))+return-EPERM;++if(old_parent_dentry->d_parent!=new_parent_dentry->d_parent)+return-EPERM;++if(strcmp(old_dentry->d_name.name,new_dentry->d_name.name))+return-EPERM;++committable_group_dentry=old_parent_dentry->d_parent;+/*+*Grabareferencetothecommittablegroupforthedurationof+*thisfunction.+*/+committable_group_item=+configfs_get_config_item(committable_group_dentry);+committable_group_subsys=+to_config_group(committable_group_item)->cg_subsys;+committable_group_ops=committable_group_item->ci_type->ct_group_ops;++item=sd->s_element;+new_parent_item=new_parent_sd->s_element;++if(WARN_ON(!is_committable_group(committable_group_item))){+/* This would be a result of a programming error in configfs. */+config_item_put(committable_group_item);+return-EPERM;+}++mutex_lock(&committable_group_subsys->su_mutex);++if((old_parent_sd->s_type&CONFIGFS_GROUP_PENDING)&&+(new_parent_sd->s_type&CONFIGFS_GROUP_LIVE))+ret=committable_group_ops->commit_item(item);+else+ret=committable_group_ops->uncommit_item(item);+if(ret)+gotoout;++spin_lock(&configfs_dirent_lock);+new_dentry->d_fsdata=sd;+list_move(&sd->s_sibling,&new_parent_sd->s_children);+item->ci_parent=new_parent_item;+d_move(old_dentry,new_dentry);+spin_unlock(&configfs_dirent_lock);++out:+mutex_unlock(&committable_group_subsys->su_mutex);+config_item_put(committable_group_item);++returnret;+}+conststructinode_operationsconfigfs_dir_inode_operations={.mkdir=configfs_mkdir,.rmdir=configfs_rmdir,+.rename=configfs_rename,.symlink=configfs_symlink,.unlink=configfs_unlink,.lookup=configfs_lookup,
From: Bartosz Golaszewski <redacted>
Implement a new, modern GPIO testing module controlled by configfs
attributes instead of module parameters. The goal of this driver is
to provide a replacement for gpio-mockup that will be easily extensible
with new features and doesn't require reloading the module to change
the setup.
Signed-off-by: Bartosz Golaszewski <redacted>
---
Documentation/admin-guide/gpio/gpio-sim.rst | 72 ++
drivers/gpio/Kconfig | 8 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-sim.c | 878 ++++++++++++++++++++
4 files changed, 959 insertions(+)
create mode 100644 Documentation/admin-guide/gpio/gpio-sim.rst
create mode 100644 drivers/gpio/gpio-sim.c
@@ -0,0 +1,72 @@+.. SPDX-License-Identifier: GPL-2.0-or-later++Configfs GPIO Simulator+=======================++The configfs GPIO Simulator (gpio-sim) provides a way to create simulated GPIO+chips for testing purposes. The lines exposed by these chips can be accessed+using the standard GPIO character device interface as well as manipulated+using sysfs attributes.++Creating simulated chips+------------------------++The gpio-sim module registers a configfs subsystem called 'gpio-sim'. It's a+subsystem with committable items which means two subdirectories are created in+the filesystem: pending and live. For more information on configfs and+committable items, please refer to Documentation/filesystems/configfs.rst.++In order to instantiate a new simulated chip, the user needs to mkdir() a new+directory in pending/. Inside each new directory, there's a set of attributes+that can be used to configure the new chip. Once the configuration is complete,+the user needs to use rename() to move the chip to the live/ directory. This+creates and registers the new device.++In order to destroy a simulated chip, it has to be moved back to pending first+and then removed using rmdir().++Currently supported configuration attributes are:++ num_lines - an unsigned integer value defining the number of GPIO lines to+ export++ label - a string defining the label for the GPIO chip++ line_names - a list of GPIO line names in the form of quoted strings+ separated by commas, e.g.: '"foo", "bar", "", "foobar"'. The+ number of strings doesn't have to be equal to the value set in+ the num_lines attribute. If it's lower than the number of lines,+ the remaining lines are unnamed. If it's larger, the superfluous+ lines are ignored. A name of the form: '""' means the line+ should be unnamed.++Additionally two read-only attributes named 'chip_name' and 'dev_name' are+exposed in order to provide users with a mapping from configfs directories to+the actual devices created in the kernel. The former returns the name of the+GPIO device as assigned by gpiolib (i.e. "gpiochip0", "gpiochip1", etc.). The+latter returns the parent device name as defined by the gpio-sim driver (i.e.+"gpio-sim.0", "gpio-sim.1", etc.). This allows user-space to map the configfs+items both to the correct character device file as well as the associated entry+in sysfs.++Simulated GPIO chips can also be defined in device-tree. The compatible string+must be: "gpio-simulator". Supported properties are:++ "gpio-sim,label" - chip label++ "gpio-sim,nr-gpios" - number of lines++Other standard GPIO properties (like "gpio-line-names" and gpio-hog) are also+supported.++Manipulating simulated lines+----------------------------++Each simulated GPIO chip creates a sysfs attribute group under its device+directory called 'line-ctrl'. Inside each group, there's a separate attribute+for each GPIO line. The name of the attribute is of the form 'gpioX' where X+is the line's offset in the chip.++Reading from a line attribute returns the current value. Writing to it (0 or 1)+changes the configuration of the simulated pull-up/pull-down resistor+(1 - pull-up, 0 - pull-down).
@@ -0,0 +1,878 @@+// SPDX-License-Identifier: GPL-2.0-or-later+/*+*GPIOtestingdriverbasedonconfigfs.+*+*Copyright(C)2021BartoszGolaszewski<bgolaszewski@baylibre.com>+*/++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt++#include<linux/bitmap.h>+#include<linux/configfs.h>+#include<linux/device.h>+#include<linux/gpio/driver.h>+#include<linux/idr.h>+#include<linux/interrupt.h>+#include<linux/irq.h>+#include<linux/irq_sim.h>+#include<linux/mod_devicetable.h>+#include<linux/module.h>+#include<linux/mutex.h>+#include<linux/platform_device.h>+#include<linux/property.h>+#include<linux/slab.h>+#include<linux/string.h>+#include<linux/string_helpers.h>+#include<linux/sysfs.h>++#include"gpiolib.h"++/*+*Thisnormallyshouldcorrespondwiththenumberofattributesexposedover+*configfs+sentinel.+*/+#define GPIO_SIM_MAX_PROP 4++staticDEFINE_IDA(gpio_sim_ida);++structgpio_sim_chip{+structgpio_chipgc;+unsignedlong*directions;+unsignedlong*values;+unsignedlong*pulls;+structirq_domain*irq_sim;+structmutexlock;+structattribute_groupattr_group;+};++structgpio_sim_attribute{+structdevice_attributedev_attr;+unsignedintoffset;+};++staticstructgpio_sim_attribute*+to_gpio_sim_attr(structdevice_attribute*dev_attr)+{+returncontainer_of(dev_attr,structgpio_sim_attribute,dev_attr);+}++staticintgpio_sim_apply_pull(structgpio_sim_chip*chip,+unsignedintoffset,intvalue)+{+intcurr_val,irq,irq_type,ret;+structgpio_desc*desc;+structgpio_chip*gc;++gc=&chip->gc;+desc=&gc->gpiodev->descs[offset];++mutex_lock(&chip->lock);++if(test_bit(FLAG_REQUESTED,&desc->flags)&&+!test_bit(FLAG_IS_OUT,&desc->flags)){+curr_val=!!test_bit(offset,chip->values);+if(curr_val==value)+gotoset_pull;++/*+*Thisisfine-itjustmeans,nobodyislistening+*forinterruptsonthisline,otherwise+*irq_create_mapping()wouldhavebeencalledfrom+*theto_irq()callback.+*/+irq=irq_find_mapping(chip->irq_sim,offset);+if(!irq)+gotoset_value;++irq_type=irq_get_trigger_type(irq);++if((value&&(irq_type&IRQ_TYPE_EDGE_RISING))||+(!value&&(irq_type&IRQ_TYPE_EDGE_FALLING))){+ret=irq_set_irqchip_state(irq,IRQCHIP_STATE_PENDING,+true);+if(ret)+gotoset_pull;+}+}++set_value:+/* Change the value unless we're actively driving the line. */+if(!test_bit(FLAG_REQUESTED,&desc->flags)||+!test_bit(FLAG_IS_OUT,&desc->flags))+__assign_bit(offset,chip->values,value);++set_pull:+__assign_bit(offset,chip->pulls,value);+mutex_unlock(&chip->lock);+return0;+}++staticintgpio_sim_get(structgpio_chip*gc,unsignedintoffset)+{+structgpio_sim_chip*chip=gpiochip_get_data(gc);+intret;++mutex_lock(&chip->lock);+ret=!!test_bit(offset,chip->values);+mutex_unlock(&chip->lock);++returnret;+}++staticvoidgpio_sim_set(structgpio_chip*gc,unsignedintoffset,intvalue)+{+structgpio_sim_chip*chip=gpiochip_get_data(gc);++mutex_lock(&chip->lock);+__assign_bit(offset,chip->values,value);+mutex_unlock(&chip->lock);+}++staticintgpio_sim_get_multiple(structgpio_chip*gc,+unsignedlong*mask,unsignedlong*bits)+{+structgpio_sim_chip*chip=gpiochip_get_data(gc);++mutex_lock(&chip->lock);+bitmap_copy(bits,chip->values,gc->ngpio);+mutex_unlock(&chip->lock);++return0;+}++staticvoidgpio_sim_set_multiple(structgpio_chip*gc,+unsignedlong*mask,unsignedlong*bits)+{+structgpio_sim_chip*chip=gpiochip_get_data(gc);++mutex_lock(&chip->lock);+bitmap_copy(chip->values,bits,gc->ngpio);+mutex_unlock(&chip->lock);+}++staticintgpio_sim_direction_output(structgpio_chip*gc,+unsignedintoffset,intvalue)+{+structgpio_sim_chip*chip=gpiochip_get_data(gc);++mutex_lock(&chip->lock);+__clear_bit(offset,chip->directions);+__assign_bit(offset,chip->values,value);+mutex_unlock(&chip->lock);++return0;+}++staticintgpio_sim_direction_input(structgpio_chip*gc,unsignedintoffset)+{+structgpio_sim_chip*chip=gpiochip_get_data(gc);++mutex_lock(&chip->lock);+__set_bit(offset,chip->directions);+mutex_unlock(&chip->lock);++return0;+}++staticintgpio_sim_get_direction(structgpio_chip*gc,unsignedintoffset)+{+structgpio_sim_chip*chip=gpiochip_get_data(gc);+intdirection;++mutex_lock(&chip->lock);+direction=!!test_bit(offset,chip->directions);+mutex_unlock(&chip->lock);++returndirection?GPIO_LINE_DIRECTION_IN:GPIO_LINE_DIRECTION_OUT;+}++staticintgpio_sim_set_config(structgpio_chip*gc,+unsignedintoffset,unsignedlongconfig)+{+structgpio_sim_chip*chip=gpiochip_get_data(gc);++switch(pinconf_to_config_param(config)){+casePIN_CONFIG_BIAS_PULL_UP:+returngpio_sim_apply_pull(chip,offset,1);+casePIN_CONFIG_BIAS_PULL_DOWN:+returngpio_sim_apply_pull(chip,offset,0);+default:+break;+}++return-ENOTSUPP;+}++staticintgpio_sim_to_irq(structgpio_chip*gc,unsignedintoffset)+{+structgpio_sim_chip*chip=gpiochip_get_data(gc);++returnirq_create_mapping(chip->irq_sim,offset);+}++staticvoidgpio_sim_free(structgpio_chip*gc,unsignedintoffset)+{+structgpio_sim_chip*chip=gpiochip_get_data(gc);++mutex_lock(&chip->lock);+__assign_bit(offset,chip->values,!!test_bit(offset,chip->pulls));+mutex_unlock(&chip->lock);+}++staticssize_tgpio_sim_sysfs_line_show(structdevice*dev,+structdevice_attribute*attr,+char*buf)+{+structgpio_sim_attribute*line_attr=to_gpio_sim_attr(attr);+structgpio_sim_chip*chip=dev_get_drvdata(dev);+intret;++mutex_lock(&chip->lock);+ret=sprintf(buf,"%u\n",!!test_bit(line_attr->offset,chip->values));+mutex_unlock(&chip->lock);++returnret;+}++staticssize_tgpio_sim_sysfs_line_store(structdevice*dev,+structdevice_attribute*attr,+constchar*buf,size_tlen)+{+structgpio_sim_attribute*line_attr=to_gpio_sim_attr(attr);+structgpio_sim_chip*chip=dev_get_drvdata(dev);+intret,val;++if(len>2||(buf[0]!='0'&&buf[0]!='1'))+return-EINVAL;++val=buf[0]=='0'?0:1;++ret=gpio_sim_apply_pull(chip,line_attr->offset,val);+if(ret)+returnret;++returnlen;+}++staticvoidgpio_sim_mutex_destroy(void*data)+{+structmutex*lock=data;++mutex_destroy(lock);+}++staticvoidgpio_sim_sysfs_remove(void*data)+{+structgpio_sim_chip*chip=data;++sysfs_remove_group(&chip->gc.parent->kobj,&chip->attr_group);+}++staticintgpio_sim_setup_sysfs(structgpio_sim_chip*chip)+{+unsignedinti,num_lines=chip->gc.ngpio;+structdevice*dev=chip->gc.parent;+structgpio_sim_attribute*line_attr;+structdevice_attribute*dev_attr;+structattribute**attrs;+intret;++attrs=devm_kcalloc(dev,sizeof(*attrs),num_lines+1,GFP_KERNEL);+if(!attrs)+return-ENOMEM;++for(i=0;i<num_lines;i++){+line_attr=devm_kzalloc(dev,sizeof(*line_attr),GFP_KERNEL);+if(!line_attr)+return-ENOMEM;++line_attr->offset=i;++dev_attr=&line_attr->dev_attr;++dev_attr->attr.name=devm_kasprintf(dev,GFP_KERNEL,+"gpio%u",i);+if(!dev_attr->attr.name)+return-ENOMEM;++dev_attr->attr.mode=0644;++dev_attr->show=gpio_sim_sysfs_line_show;+dev_attr->store=gpio_sim_sysfs_line_store;++attrs[i]=&dev_attr->attr;+}++chip->attr_group.name="line-ctrl";+chip->attr_group.attrs=attrs;++ret=sysfs_create_group(&dev->kobj,&chip->attr_group);+if(ret)+returnret;++returndevm_add_action_or_reset(dev,gpio_sim_sysfs_remove,chip);+}++staticintgpio_sim_probe(structplatform_device*pdev)+{+structdevice*dev=&pdev->dev;+structgpio_sim_chip*chip;+structgpio_chip*gc;+constchar*label;+u32num_lines;+intret;++ret=device_property_read_u32(dev,"gpio-sim,nr-gpios",&num_lines);+if(ret)+returnret;++ret=device_property_read_string(dev,"gpio-sim,label",&label);+if(ret)+label=dev_name(dev);++chip=devm_kzalloc(dev,sizeof(*chip),GFP_KERNEL);+if(!chip)+return-ENOMEM;++chip->directions=devm_bitmap_zalloc(dev,num_lines,GFP_KERNEL);+if(!chip->directions)+return-ENOMEM;++/* Default to input mode. */+bitmap_fill(chip->directions,num_lines);++chip->values=devm_bitmap_zalloc(dev,num_lines,GFP_KERNEL);+if(!chip->values)+return-ENOMEM;++chip->pulls=devm_bitmap_zalloc(dev,num_lines,GFP_KERNEL);+if(!chip->pulls)+return-ENOMEM;++chip->irq_sim=devm_irq_domain_create_sim(dev,NULL,num_lines);+if(IS_ERR(chip->irq_sim))+returnPTR_ERR(chip->irq_sim);++mutex_init(&chip->lock);+ret=devm_add_action_or_reset(dev,gpio_sim_mutex_destroy,+&chip->lock);+if(ret)+returnret;++gc=&chip->gc;+gc->base=-1;+gc->ngpio=num_lines;+gc->label=label;+gc->owner=THIS_MODULE;+gc->parent=dev;+gc->get=gpio_sim_get;+gc->set=gpio_sim_set;+gc->get_multiple=gpio_sim_get_multiple;+gc->set_multiple=gpio_sim_set_multiple;+gc->direction_output=gpio_sim_direction_output;+gc->direction_input=gpio_sim_direction_input;+gc->get_direction=gpio_sim_get_direction;+gc->set_config=gpio_sim_set_config;+gc->to_irq=gpio_sim_to_irq;+gc->free=gpio_sim_free;++ret=devm_gpiochip_add_data(dev,gc,chip);+if(ret)+returnret;++/* Used by sysfs and configfs callbacks. */+dev_set_drvdata(dev,chip);++ret=gpio_sim_setup_sysfs(chip);+if(ret)+returnret;++return0;+}++staticconststructof_device_idgpio_sim_of_match[]={+{.compatible="gpio-simulator"},+{}+};+MODULE_DEVICE_TABLE(of,gpio_sim_of_match);++staticstructplatform_drivergpio_sim_driver={+.driver={+.name="gpio-sim",+},+.probe=gpio_sim_probe,+};++structgpio_sim_chip_config{+structconfig_itemitem;++/*+*IfpdevisNULL,theitemis'pending'(waitingforconfiguration).+*Oncethepointerisassigned,thedevicehasbeencreatedandthe+*itemis'live'.+*/+structplatform_device*pdev;++/*+*Eachconfigfsfilesystemoperationisprotectedwiththesubsystem+*mutex.Eachseparateattributeisprotectedwiththebuffermutex.+*Thisstructurehowevercanbemodifiedbycallbacksofdifferent+*attributessoweneedanotherlock.+*/+structmutexlock;++charlabel[32];+unsignedintnum_lines;+char**line_names;+unsignedintnum_line_names;+};++staticstructgpio_sim_chip_config*+to_gpio_sim_chip_config(structconfig_item*item)+{+returncontainer_of(item,structgpio_sim_chip_config,item);+}++staticssize_tgpio_sim_config_dev_name_show(structconfig_item*item,+char*page)+{+structgpio_sim_chip_config*config=to_gpio_sim_chip_config(item);+structplatform_device*pdev;+intret;++mutex_lock(&config->lock);+pdev=config->pdev;+if(pdev&&device_is_bound(&pdev->dev))+ret=sprintf(page,"%s\n",dev_name(&pdev->dev));+else+ret=-ENODEV;+mutex_unlock(&config->lock);++returnret;+}++CONFIGFS_ATTR_RO(gpio_sim_config_,dev_name);++staticssize_tgpio_sim_config_chip_name_show(structconfig_item*item,+char*page)+{+structgpio_sim_chip_config*config=to_gpio_sim_chip_config(item);+structplatform_device*pdev;+structgpio_sim_chip*chip;+intret;++mutex_lock(&config->lock);+pdev=config->pdev;+if(pdev&&device_is_bound(&pdev->dev)){+chip=dev_get_drvdata(&pdev->dev);+ret=sprintf(page,"%s\n",dev_name(&chip->gc.gpiodev->dev));+}else{+ret=-ENODEV;+}+mutex_unlock(&config->lock);++returnret;+}++CONFIGFS_ATTR_RO(gpio_sim_config_,chip_name);++staticssize_tgpio_sim_config_label_show(structconfig_item*item,char*page)+{+structgpio_sim_chip_config*config=to_gpio_sim_chip_config(item);+intret;++mutex_lock(&config->lock);+ret=sprintf(page,"%s\n",config->label);+mutex_unlock(&config->lock);++returnret;+}++staticssize_tgpio_sim_config_label_store(structconfig_item*item,+constchar*page,size_tcount)+{+structgpio_sim_chip_config*config=to_gpio_sim_chip_config(item);+char*dup,*trimmed;+intret;++mutex_lock(&config->lock);++if(config->pdev){+mutex_unlock(&config->lock);+return-EBUSY;+}++dup=kstrndup(page,count,GFP_KERNEL);+if(!dup){+mutex_unlock(&config->lock);+return-ENOMEM;+}++trimmed=strstrip(dup);+ret=snprintf(config->label,sizeof(config->label),"%s",trimmed);+kfree(dup);+if(ret<0){+mutex_unlock(&config->lock);+returnret;+}++mutex_unlock(&config->lock);+returncount;+}++CONFIGFS_ATTR(gpio_sim_config_,label);++staticssize_tgpio_sim_config_num_lines_show(structconfig_item*item,+char*page)+{+structgpio_sim_chip_config*config=to_gpio_sim_chip_config(item);+intret;++mutex_lock(&config->lock);+ret=sprintf(page,"%u\n",config->num_lines);+mutex_unlock(&config->lock);++returnret;+}++staticssize_tgpio_sim_config_num_lines_store(structconfig_item*item,+constchar*page,size_tcount)+{+structgpio_sim_chip_config*config=to_gpio_sim_chip_config(item);+unsignedintnum_lines;+intret;++mutex_lock(&config->lock);++if(config->pdev){+mutex_unlock(&config->lock);+return-EBUSY;+}++ret=kstrtouint(page,10,&num_lines);+if(ret){+mutex_unlock(&config->lock);+returnret;+}++if(num_lines==0){+mutex_unlock(&config->lock);+return-EINVAL;+}++config->num_lines=num_lines;++mutex_unlock(&config->lock);+returncount;+}++CONFIGFS_ATTR(gpio_sim_config_,num_lines);++staticssize_tgpio_sim_config_line_names_show(structconfig_item*item,+char*page)+{+structgpio_sim_chip_config*config=to_gpio_sim_chip_config(item);+intret,i,written=0;++mutex_lock(&config->lock);++if(!config->line_names){+mutex_unlock(&config->lock);+returnsprintf(page,"\n");+}++for(i=0;i<config->num_line_names;i++){+ret=sprintf(page+written,+i<config->num_line_names-1?+"\"%s\", ":"\"%s\"\n",+config->line_names[i]?:"");+if(ret<0){+mutex_unlock(&config->lock);+returnret;+}++written+=ret;+}++mutex_unlock(&config->lock);+returnwritten;+}++staticssize_tgpio_sim_config_line_names_store(structconfig_item*item,+constchar*page,size_tcount)+{+structgpio_sim_chip_config*config=to_gpio_sim_chip_config(item);+unsignedintnum_new_names=1,num_old_names,name_idx=0;+boolin_quote=false,got_comma=true;+char**new_names,**old_names,*name,c;+constchar*start=page;+size_tpos,name_len;+interr=-EINVAL;++mutex_lock(&config->lock);++if(config->pdev){+mutex_unlock(&config->lock);+return-EBUSY;+}++/*+*Linenamesarestoredinapointerarraysothatwecaneasily+*passthemdowntotheGPIOsubsystemina"gpio-line-names"+*property.+*+*Linenamesmustbepassedasalistofquotednamesseparatedby+*commas,forexample:'"foo","bar","foobar"'.+*/++for(pos=0;pos<count;pos++){+/*+*Justcountthecommasandassumethenumberifstrings+*equalsthenumberofcommas+1.Iftheformatiswrong+*we'llbailoutanyway.+*/+if(page[pos]==',')+num_new_names++;+}++new_names=kcalloc(num_new_names,sizeof(char*),GFP_KERNEL);+if(!new_names){+mutex_unlock(&config->lock);+return-ENOMEM;+}++/*+*FIXMEIfanyoneknowsabetterwaytoparsethat-pleaseletme+*know.+*/+for(pos=0;pos<count;pos++){+c=page[pos];++if(in_quote){+if(c=='"'){+/* This is the end of the name. */+in_quote=got_comma=false;+name_len=(page+pos)-start;+if(name_len==0){+/* Name is empty (passed as ""). */+name_idx++;+continue;+}++name=kzalloc(name_len+1,GFP_KERNEL);+if(!name){+err=-ENOMEM;+gotoerr_out;+}++memcpy(name,start,name_len);+new_names[name_idx++]=name;+}+}else{+if(c=='"'){+/* Enforce separating names with commas. */+if(!got_comma)+gotoerr_out;++start=page+pos+1;+in_quote=true;+}elseif(c==','){+if(!got_comma)+got_comma=true;+else+/* Double commas are not allowed. */+gotoerr_out;+}elseif(!isspace(c)){+gotoerr_out;+}+}+}++/*+*Endofinputsanitychecks,mustnothaveacommaattheendand+*musthavefinishedscanningthelastname.+*/+if(in_quote||got_comma)+gotoerr_out;++old_names=config->line_names;+num_old_names=config->num_line_names;+config->line_names=new_names;+config->num_line_names=num_new_names;++mutex_unlock(&config->lock);+kfree_strarray(old_names,num_old_names);+returncount;++err_out:+mutex_unlock(&config->lock);+kfree_strarray(new_names,name_idx);+returnerr;+}++CONFIGFS_ATTR(gpio_sim_config_,line_names);++staticstructconfigfs_attribute*gpio_sim_config_attrs[]={+&gpio_sim_config_attr_dev_name,+&gpio_sim_config_attr_chip_name,+&gpio_sim_config_attr_label,+&gpio_sim_config_attr_num_lines,+&gpio_sim_config_attr_line_names,+NULL+};++staticvoidgpio_sim_chip_config_release(structconfig_item*item)+{+structgpio_sim_chip_config*config=to_gpio_sim_chip_config(item);++mutex_destroy(&config->lock);+kfree_strarray(config->line_names,config->num_line_names);+kfree(config);+}++staticstructconfigfs_item_operationsgpio_sim_config_item_ops={+.release=gpio_sim_chip_config_release,+};++staticconststructconfig_item_typegpio_sim_chip_config_type={+.ct_item_ops=&gpio_sim_config_item_ops,+.ct_attrs=gpio_sim_config_attrs,+.ct_owner=THIS_MODULE,+};++staticstructconfig_item*+gpio_sim_config_make_item(structconfig_group*group,constchar*name)+{+structgpio_sim_chip_config*config;++config=kzalloc(sizeof(*config),GFP_KERNEL);+if(!config)+returnERR_PTR(-ENOMEM);++config_item_init_type_name(&config->item,name,+&gpio_sim_chip_config_type);+config->num_lines=1;+mutex_init(&config->lock);++return&config->item;+}++staticintgpio_sim_config_commit_item(structconfig_item*item)+{+structgpio_sim_chip_config*config=to_gpio_sim_chip_config(item);+structproperty_entryproperties[GPIO_SIM_MAX_PROP];+structplatform_device_infopdevinfo;+structplatform_device*pdev;+unsignedintprop_idx=0;++memset(&pdevinfo,0,sizeof(pdevinfo));+memset(properties,0,sizeof(properties));++mutex_lock(&config->lock);++properties[prop_idx++]=PROPERTY_ENTRY_U32("gpio-sim,nr-gpios",+config->num_lines);++if(config->label[0]!='\0')+properties[prop_idx++]=PROPERTY_ENTRY_STRING("gpio-sim,label",+config->label);++if(config->line_names)+properties[prop_idx++]=PROPERTY_ENTRY_STRING_ARRAY_LEN(+"gpio-line-names",+config->line_names,+config->num_line_names);++pdevinfo.id=ida_alloc(&gpio_sim_ida,GFP_KERNEL);+if(pdevinfo.id<0){+mutex_unlock(&config->lock);+returnpdevinfo.id;+}++pdevinfo.name="gpio-sim";+pdevinfo.properties=properties;++pdev=platform_device_register_full(&pdevinfo);+if(IS_ERR(pdev)){+ida_free(&gpio_sim_ida,pdevinfo.id);+mutex_unlock(&config->lock);+returnPTR_ERR(pdev);+}++config->pdev=pdev;+mutex_unlock(&config->lock);++return0;+}++staticintgpio_sim_config_uncommit_item(structconfig_item*item)+{+structgpio_sim_chip_config*config=to_gpio_sim_chip_config(item);+intid;++mutex_lock(&config->lock);+id=config->pdev->id;+platform_device_unregister(config->pdev);+config->pdev=NULL;+ida_free(&gpio_sim_ida,id);+mutex_unlock(&config->lock);++return0;+}++staticstructconfigfs_group_operationsgpio_sim_config_group_ops={+.make_item=gpio_sim_config_make_item,+.commit_item=gpio_sim_config_commit_item,+.uncommit_item=gpio_sim_config_uncommit_item,+};++staticconststructconfig_item_typegpio_sim_config_type={+.ct_group_ops=&gpio_sim_config_group_ops,+.ct_owner=THIS_MODULE,+};++staticstructconfigfs_subsystemgpio_sim_config_subsys={+.su_group={+.cg_item={+.ci_namebuf="gpio-sim",+.ci_type=&gpio_sim_config_type,+},+},+};++staticint__initgpio_sim_init(void)+{+intret;++ret=platform_driver_register(&gpio_sim_driver);+if(ret){+pr_err("Error %d while registering the platform driver\n",ret);+returnret;+}++config_group_init(&gpio_sim_config_subsys.su_group);+mutex_init(&gpio_sim_config_subsys.su_mutex);+ret=configfs_register_subsystem(&gpio_sim_config_subsys);+if(ret){+pr_err("Error %d while registering the configfs subsystem %s\n",+ret,gpio_sim_config_subsys.su_group.cg_item.ci_namebuf);+mutex_destroy(&gpio_sim_config_subsys.su_mutex);+platform_driver_unregister(&gpio_sim_driver);+returnret;+}++return0;+}+module_init(gpio_sim_init);++staticvoid__exitgpio_sim_exit(void)+{+configfs_unregister_subsystem(&gpio_sim_config_subsys);+mutex_destroy(&gpio_sim_config_subsys.su_mutex);+platform_driver_unregister(&gpio_sim_driver);+}+module_exit(gpio_sim_exit);++MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");+MODULE_DESCRIPTION("GPIO Simulator Module");+MODULE_LICENSE("GPL");
From: Bartosz Golaszewski <redacted>
Add a set of tests for the new gpio-sim module. This is a pure shell
test-suite and uses the helper programs available in the gpio selftests
directory. These test-cases only test the functionalities exposed by the
gpio-sim driver, not those handled by core gpiolib code.
Signed-off-by: Bartosz Golaszewski <redacted>
---
tools/testing/selftests/gpio/Makefile | 2 +-
tools/testing/selftests/gpio/config | 1 +
tools/testing/selftests/gpio/gpio-sim.sh | 229 +++++++++++++++++++++++
3 files changed, 231 insertions(+), 1 deletion(-)
create mode 100755 tools/testing/selftests/gpio/gpio-sim.sh
@@ -0,0 +1,229 @@+#!/bin/sh+# SPDX-License-Identifier: GPL-2.0+# Copyright (C) 2021 Bartosz Golaszewski <bgolaszewski@baylibre.com>++BASE_DIR=`dirname$0`+CONFIGFS_DIR="/sys/kernel/config/gpio-sim"+PENDING_DIR=$CONFIGFS_DIR/pending+LIVE_DIR=$CONFIGFS_DIR/live+MODULE="gpio-sim"++fail(){+echo"$*">&2+echo"GPIO $MODULE test FAIL"+exit1+}++skip(){+echo"$*">&2+echo"GPIO $MODULE test SKIP"+exit4+}++configfs_cleanup(){+forDIRin`ls$LIVE_DIR`;do+mv$LIVE_DIR/$DIR$PENDING_DIR+done++forDIRin`ls$PENDING_DIR`;do+rmdir$PENDING_DIR/$DIR+done+}++create_pending_chip(){+localNAME="$1"+localLABEL="$2"+localNUM_LINES="$3"+localLINE_NAMES="$4"+localCHIP_DIR="$PENDING_DIR/$NAME"++mkdir$CHIP_DIR+test-n"$LABEL"&&echo$LABEL>$CHIP_DIR/label+test-n"$NUM_LINES"&&echo$NUM_LINES>$CHIP_DIR/num_lines+if[-n"$LINE_NAMES"];then+echo$LINE_NAMES2>/dev/null>$CHIP_DIR/line_names+# This one can fail+if["$?"-ne"0"];then+return1+fi+fi+}++create_live_chip(){+localCHIP_DIR="$PENDING_DIR/$1"++create_pending_chip"$@"||fail"unable to create the chip configfs item"+mv$CHIP_DIR$LIVE_DIR||fail"unable to commit the chip configfs item"+}++remove_pending_chip(){+localNAME="$1"++rmdir$PENDING_DIR/$NAME||fail"unable to remove the chip configfs item"+}++remove_live_chip(){+localNAME="$1"++mv$LIVE_DIR/$NAME$PENDING_DIR||fail"unable to uncommit the chip configfs item"+remove_pending_chip"$@"+}++configfs_chip_name(){+localCHIP="$1"++cat$LIVE_DIR/$CHIP/chip_name2>/dev/null||return1+}++configfs_dev_name(){+localCHIP="$1"++cat$LIVE_DIR/$CHIP/dev_name2>/dev/null||return1+}++get_chip_num_lines(){+localCHIP="$1"++$BASE_DIR/gpio-chip-info/dev/`configfs_chip_name$CHIP`num-lines+}++get_chip_label(){+localCHIP="$1"++$BASE_DIR/gpio-chip-info/dev/`configfs_chip_name$CHIP`label+}++get_line_name(){+localCHIP="$1"+localOFFSET="$2"++$BASE_DIR/gpio-line-name/dev/`configfs_chip_name$CHIP`$OFFSET+}++sysfs_set_pull(){+localCHIP="$1"+localOFFSET="$2"+localPULL="$3"+localSYSFSPATH="/sys/devices/platform/`configfs_dev_name $CHIP`/line-ctrl/gpio$OFFSET"++echo$PULL>$SYSFSPATH+}++# Load the gpio-sim module. This will pull in configfs if needed too.+modprobegpio-sim||skip"unable to load the gpio-sim module"+# Make sure configfs is mounted at /sys/kernel/config. Wait a bit if needed.+forIDXin`seq5`;do+if["$IDX"-eq"5"];then+skip"configfs not mounted at /sys/kernel/config"+fi++mountpoint-q/sys/kernel/config&&break+sleep0.1+done+# If the module was already loaded: remove all previous chips+configfs_cleanup++trap"exit 1"SIGTERMSIGINT+trapconfigfs_cleanupEXIT++echo"1. chip_name and dev_name attributes"++echo"1.1. Chip name is communicated to user"+create_live_chipchip+test-n`cat$LIVE_DIR/chip/chip_name`||fail"chip_name doesn't work"+remove_live_chipchip++echo"1.2. chip_name returns an error if chip is still pending"+create_pending_chipchip+configfs_chip_namechip&&fail"chip_name doesn't return error for a pending chip"+remove_pending_chipchip++echo"1.3. Device name is communicated to user"+create_live_chipchip+test-n`cat$LIVE_DIR/chip/dev_name`||fail"dev_name doesn't work"+remove_live_chipchip++echo"1.4. dev_name returns an error if chip is still pending"+create_pending_chipchip+configfs_dev_namechip&&fail"dev_name doesn't return error for a pending chip"+remove_pending_chipchip++echo"2. Creating simulated chips"++echo"2.1. Default number of lines is 1"+create_live_chipchip+test"`get_chip_num_lines chip`"="1"||fail"default number of lines is not 1"+remove_live_chipchip++echo"2.2. Number of lines can be specified"+create_live_chipchiptest-label16+test"`get_chip_num_lines chip`"="16"||fail"number of lines is not 16"+remove_live_chipchip++echo"2.3. Label can be set"+create_live_chipchipfoobar+test"`get_chip_label chip`"="foobar"||fail"label is incorrect"+remove_live_chipchip++echo"2.4. Label can be left empty"+create_live_chipchip+test-z"`cat $LIVE_DIR/chip/label`"||fail"label is not empty"+remove_live_chipchip++echo"2.5. Line names can be configured"+create_live_chipchiptest-label16'"foo", "", "bar"'+test"`get_line_name chip 0`"="foo"||fail"line name is incorrect"+test"`get_line_name chip 2`"="bar"||fail"line name is incorrect"+remove_live_chipchip++echo"2.6. Errors in line names are detected"+create_pending_chipchiptest-label8'"foo", bar'&&fail"incorrect line name accepted"+remove_pending_chipchip+create_pending_chipchiptest-label8'"foo" "bar"'&&fail"incorrect line name accepted"+remove_pending_chipchip++echo"2.7. Multiple chips can be created"+create_live_chipchip0+create_live_chipchip1+create_live_chipchip2+remove_live_chipchip0+remove_live_chipchip1+remove_live_chipchip2++echo"3. Controlling simulated chips"++echo"3.3. Pull can be set over sysfs"+create_live_chipchiptest-label8+sysfs_set_pullchip01+$BASE_DIR/gpio-mockup-cdev/dev/`configfs_chip_namechip`0+test"$?"="1"||fail"pull set incorrectly"+sysfs_set_pullchip00+$BASE_DIR/gpio-mockup-cdev/dev/`configfs_chip_namechip`1+test"$?"="0"||fail"pull set incorrectly"+remove_live_chipchip++echo"3.4. Incorrect input in sysfs is rejected"+create_live_chipchiptest-label8+SYSFS_PATH="/sys/devices/platform/`configfs_dev_name chip`/line-ctrl/gpio0"+echo2>$SYSFS_PATH2>/dev/null&&fail"invalid input not detectec"+remove_live_chipchip++echo"4. Simulated GPIO chips are functional"++echo"4.1. Values can be read from sysfs"+create_live_chipchiptest-label8+SYSFS_PATH="/sys/devices/platform/`configfs_dev_name chip`/line-ctrl/gpio0"+test`cat$SYSFS_PATH`="0"||fail"incorrect value read from sysfs"+$BASE_DIR/gpio-mockup-cdev-s1/dev/`configfs_chip_namechip`0&+sleep0.1# FIXME Any better way?+test`cat$SYSFS_PATH`="1"||fail"incorrect value read from sysfs"+kill$!+remove_live_chipchip++echo"4.2. Bias settings work correctly"+create_live_chipchiptest-label8+$BASE_DIR/gpio-mockup-cdev-bpull-up/dev/`configfs_chip_namechip`0+test`cat$SYSFS_PATH`="1"||fail"bias setting does not work"+remove_live_chipchip++echo"GPIO $MODULE test PASS"
From: Bartosz Golaszewski <redacted>
Add a simple program that allows to read GPIO line names from the
character device. This will be used in gpio-sim selftests.
Signed-off-by: Bartosz Golaszewski <redacted>
---
tools/testing/selftests/gpio/.gitignore | 1 +
tools/testing/selftests/gpio/Makefile | 2 +-
tools/testing/selftests/gpio/gpio-line-name.c | 55 +++++++++++++++++++
3 files changed, 57 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/gpio/gpio-line-name.c
@@ -0,0 +1,55 @@+// SPDX-License-Identifier: GPL-2.0-or-later+/*+*GPIOcharacterdevicehelperforreadinglinenames.+*+*Copyright(C)2021BartoszGolaszewski<bgolaszewski@baylibre.com>+*/++#include<fcntl.h>+#include<linux/gpio.h>+#include<stdio.h>+#include<stdlib.h>+#include<string.h>+#include<sys/ioctl.h>+#include<sys/types.h>++staticvoidprint_usage(void)+{+printf("usage:\n");+printf(" gpio-line-name <chip path> <line offset>\n");+}++intmain(intargc,char**argv)+{+structgpio_v2_line_infoinfo;+intfd,ret;+char*endp;++if(argc!=3){+print_usage();+returnEXIT_FAILURE;+}++fd=open(argv[1],O_RDWR);+if(fd<0){+perror("unable to open the GPIO chip");+returnEXIT_FAILURE;+}++memset(&info,0,sizeof(info));+info.offset=strtoul(argv[2],&endp,10);+if(*endp!='\0'){+print_usage();+returnEXIT_FAILURE;+}++ret=ioctl(fd,GPIO_V2_GET_LINEINFO_IOCTL,&info);+if(ret){+perror("line info ioctl failed");+returnEXIT_FAILURE;+}++printf("%s\n",info.name);++returnEXIT_SUCCESS;+}
From: Bartosz Golaszewski <redacted>
Add a simple program that allows to retrieve chip properties from the
GPIO character device. This will be used in gpio-sim selftests.
Signed-off-by: Bartosz Golaszewski <redacted>
---
tools/testing/selftests/gpio/.gitignore | 1 +
tools/testing/selftests/gpio/Makefile | 2 +-
tools/testing/selftests/gpio/gpio-chip-info.c | 57 +++++++++++++++++++
3 files changed, 59 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/gpio/gpio-chip-info.c
@@ -0,0 +1,57 @@+// SPDX-License-Identifier: GPL-2.0-or-later+/*+*GPIOcharacterdevicehelperforreadingchipinformation.+*+*Copyright(C)2021BartoszGolaszewski<bgolaszewski@baylibre.com>+*/++#include<fcntl.h>+#include<linux/gpio.h>+#include<stdio.h>+#include<stdlib.h>+#include<string.h>+#include<sys/ioctl.h>+#include<sys/types.h>++staticvoidprint_usage(void)+{+printf("usage:\n");+printf(" gpio-chip-info <chip path> [name|label|num-lines]\n");+}++intmain(intargc,char**argv)+{+structgpiochip_infoinfo;+intfd,ret;++if(argc!=3){+print_usage();+returnEXIT_FAILURE;+}++fd=open(argv[1],O_RDWR);+if(fd<0){+perror("unable to open the GPIO chip");+returnEXIT_FAILURE;+}++memset(&info,0,sizeof(info));+ret=ioctl(fd,GPIO_GET_CHIPINFO_IOCTL,&info);+if(ret){+perror("chip info ioctl failed");+returnEXIT_FAILURE;+}++if(strcmp(argv[2],"name")==0){+printf("%s\n",info.name);+}elseif(strcmp(argv[2],"label")==0){+printf("%s\n",info.label);+}elseif(strcmp(argv[2],"num-lines")==0){+printf("%u\n",info.lines);+}else{+fprintf(stderr,"unknown command: %s\n",argv[2]);+returnEXIT_FAILURE;+}++returnEXIT_SUCCESS;+}
From: Bartosz Golaszewski <redacted>
Export the symbol for device_is_bound() so that we can use it in gpio-sim
to check if the simulated GPIO chip is bound before fetching its driver
data from configfs callbacks in order to retrieve the name of the GPIO
chip device.
Signed-off-by: Bartosz Golaszewski <redacted>
---
drivers/base/dd.c | 1 +
1 file changed, 1 insertion(+)
Logically better to have
int __bitmap_equal(const unsigned long *bitmap1, const unsigned long *bitmap2,
unsigned int nbits);
quoted hunk
+bool __pure __bitmap_or_equal(const unsigned long *src1,
+ const unsigned long *src2,
+ const unsigned long *src3,
+ unsigned int nbits);
+void __bitmap_complement(unsigned long *dst, const unsigned long *src,
+ unsigned int nbits);
+void __bitmap_shift_right(unsigned long *dst, const unsigned long *src,
+ unsigned int shift, unsigned int nbits);
+void __bitmap_shift_left(unsigned long *dst, const unsigned long *src,
+ unsigned int shift, unsigned int nbits);
+void bitmap_cut(unsigned long *dst, const unsigned long *src,
+ unsigned int first, unsigned int cut, unsigned int nbits);
+int __bitmap_and(unsigned long *dst, const unsigned long *bitmap1,
+ const unsigned long *bitmap2, unsigned int nbits);
+void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
+ const unsigned long *bitmap2, unsigned int nbits);
+void __bitmap_xor(unsigned long *dst, const unsigned long *bitmap1,
+ const unsigned long *bitmap2, unsigned int nbits);
+int __bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
+ const unsigned long *bitmap2, unsigned int nbits);
+void __bitmap_replace(unsigned long *dst,
+ const unsigned long *old, const unsigned long *new,
+ const unsigned long *mask, unsigned int nbits);
+int __bitmap_intersects(const unsigned long *bitmap1,
const unsigned long *bitmap2, unsigned int nbits);
-extern void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
- const unsigned long *bitmap2, unsigned int nbits);
-extern void __bitmap_xor(unsigned long *dst, const unsigned long *bitmap1,
- const unsigned long *bitmap2, unsigned int nbits);
-extern int __bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
- const unsigned long *bitmap2, unsigned int nbits);
-extern void __bitmap_replace(unsigned long *dst,
- const unsigned long *old, const unsigned long *new,
- const unsigned long *mask, unsigned int nbits);
-extern int __bitmap_intersects(const unsigned long *bitmap1,
- const unsigned long *bitmap2, unsigned int nbits);
-extern int __bitmap_subset(const unsigned long *bitmap1,
- const unsigned long *bitmap2, unsigned int nbits);
-extern int __bitmap_weight(const unsigned long *bitmap, unsigned int nbits);
-extern void __bitmap_set(unsigned long *map, unsigned int start, int len);
-extern void __bitmap_clear(unsigned long *map, unsigned int start, int len);
-
-extern unsigned long bitmap_find_next_zero_area_off(unsigned long *map,
- unsigned long size,
- unsigned long start,
- unsigned int nr,
- unsigned long align_mask,
- unsigned long align_offset);
+int __bitmap_subset(const unsigned long *bitmap1,
+ const unsigned long *bitmap2, unsigned int nbits);
+int __bitmap_weight(const unsigned long *bitmap, unsigned int nbits);
+void __bitmap_set(unsigned long *map, unsigned int start, int len);
+void __bitmap_clear(unsigned long *map, unsigned int start, int len);
+
+unsigned long bitmap_find_next_zero_area_off(unsigned long *map,
+ unsigned long size,
+ unsigned long start,
+ unsigned int nr,
+ unsigned long align_mask,
+ unsigned long align_offset);
/**
* bitmap_find_next_zero_area - find a contiguous aligned zero area
@@ -190,33 +189,33 @@ bitmap_find_next_zero_area(unsigned long *map, align_mask, 0); }-extern int bitmap_parse(const char *buf, unsigned int buflen,+int bitmap_parse(const char *buf, unsigned int buflen, unsigned long *dst, int nbits);
Can be
int bitmap_parse(const char *buf, unsigned int buflen, unsigned long *dst,
int nbits);
And I wonder why nbits here is signed.
-extern int bitmap_parse_user(const char __user *ubuf, unsigned int ulen,
+int bitmap_parse_user(const char __user *ubuf, unsigned int ulen,
unsigned long *dst, int nbits);
Ditto.
-extern int bitmap_parselist(const char *buf, unsigned long *maskp,
+int bitmap_parselist(const char *buf, unsigned long *maskp,
int nmaskbits);
Now can be one line.
-extern int bitmap_parselist_user(const char __user *ubuf, unsigned int ulen,
+int bitmap_parselist_user(const char __user *ubuf, unsigned int ulen,
unsigned long *dst, int nbits);
-extern void bitmap_remap(unsigned long *dst, const unsigned long *src,
+void bitmap_remap(unsigned long *dst, const unsigned long *src,
const unsigned long *old, const unsigned long *new, unsigned int nbits);
-extern int bitmap_bitremap(int oldbit,
+int bitmap_bitremap(int oldbit,
const unsigned long *old, const unsigned long *new, int bits);
More logical
int bitmap_bitremap(int oldbit, const unsigned long *old,
const unsigned long *new, int bits);
Or even
int bitmap_bitremap(int oldbit, const unsigned long *old, const unsigned long *new,
int bits);
quoted hunk
-extern void bitmap_onto(unsigned long *dst, const unsigned long *orig,
+void bitmap_onto(unsigned long *dst, const unsigned long *orig,
const unsigned long *relmap, unsigned int bits);
-extern void bitmap_fold(unsigned long *dst, const unsigned long *orig,
+void bitmap_fold(unsigned long *dst, const unsigned long *orig,
unsigned int sz, unsigned int nbits);
-extern int bitmap_find_free_region(unsigned long *bitmap, unsigned int bits, int order);
-extern void bitmap_release_region(unsigned long *bitmap, unsigned int pos, int order);
-extern int bitmap_allocate_region(unsigned long *bitmap, unsigned int pos, int order);
+int bitmap_find_free_region(unsigned long *bitmap, unsigned int bits, int order);
+void bitmap_release_region(unsigned long *bitmap, unsigned int pos, int order);
+int bitmap_allocate_region(unsigned long *bitmap, unsigned int pos, int order);
#ifdef __BIG_ENDIAN
-extern void bitmap_copy_le(unsigned long *dst, const unsigned long *src, unsigned int nbits);
+void bitmap_copy_le(unsigned long *dst, const unsigned long *src, unsigned int nbits);
#else
#define bitmap_copy_le bitmap_copy
#endif
-extern unsigned int bitmap_ord_to_pos(const unsigned long *bitmap, unsigned int ord, unsigned int nbits);
-extern int bitmap_print_to_pagebuf(bool list, char *buf,
+unsigned int bitmap_ord_to_pos(const unsigned long *bitmap, unsigned int ord, unsigned int nbits);
+int bitmap_print_to_pagebuf(bool list, char *buf,
const unsigned long *maskp, int nmaskbits);
#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1)))
@@ -265,9 +264,9 @@ static inline void bitmap_copy_clear_tail(unsigned long *dst, * therefore conversion is not needed when copying data from/to arrays of u32. */ #if BITS_PER_LONG == 64-extern void bitmap_from_arr32(unsigned long *bitmap, const u32 *buf,+void bitmap_from_arr32(unsigned long *bitmap, const u32 *buf, unsigned int nbits);
One line?
-extern void bitmap_to_arr32(u32 *buf, const unsigned long *bitmap,
+void bitmap_to_arr32(u32 *buf, const unsigned long *bitmap,
unsigned int nbits);
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Date: 2021-03-04 13:18:11
On Thu, Mar 04, 2021 at 11:24:49AM +0100, Bartosz Golaszewski wrote:
From: Bartosz Golaszewski <redacted>
Implement a new, modern GPIO testing module controlled by configfs
attributes instead of module parameters. The goal of this driver is
to provide a replacement for gpio-mockup that will be easily extensible
with new features and doesn't require reloading the module to change
the setup.
Shall we put a reference to this in the gpio-mockup documentation and mark the
latter deprecated?
@@ -0,0 +1,72 @@+.. SPDX-License-Identifier: GPL-2.0-or-later++Configfs GPIO Simulator+=======================++The configfs GPIO Simulator (gpio-sim) provides a way to create simulated GPIO+chips for testing purposes. The lines exposed by these chips can be accessed+using the standard GPIO character device interface as well as manipulated+using sysfs attributes.++Creating simulated chips+------------------------++The gpio-sim module registers a configfs subsystem called 'gpio-sim'. It's a+subsystem with committable items which means two subdirectories are created in+the filesystem: pending and live. For more information on configfs and+committable items, please refer to Documentation/filesystems/configfs.rst.++In order to instantiate a new simulated chip, the user needs to mkdir() a new+directory in pending/. Inside each new directory, there's a set of attributes+that can be used to configure the new chip. Once the configuration is complete,+the user needs to use rename() to move the chip to the live/ directory. This+creates and registers the new device.++In order to destroy a simulated chip, it has to be moved back to pending first+and then removed using rmdir().++Currently supported configuration attributes are:++ num_lines - an unsigned integer value defining the number of GPIO lines to+ export++ label - a string defining the label for the GPIO chip++ line_names - a list of GPIO line names in the form of quoted strings+ separated by commas, e.g.: '"foo", "bar", "", "foobar"'. The+ number of strings doesn't have to be equal to the value set in+ the num_lines attribute. If it's lower than the number of lines,+ the remaining lines are unnamed. If it's larger, the superfluous+ lines are ignored. A name of the form: '""' means the line+ should be unnamed.++Additionally two read-only attributes named 'chip_name' and 'dev_name' are+exposed in order to provide users with a mapping from configfs directories to+the actual devices created in the kernel. The former returns the name of the+GPIO device as assigned by gpiolib (i.e. "gpiochip0", "gpiochip1", etc.). The+latter returns the parent device name as defined by the gpio-sim driver (i.e.+"gpio-sim.0", "gpio-sim.1", etc.). This allows user-space to map the configfs+items both to the correct character device file as well as the associated entry+in sysfs.++Simulated GPIO chips can also be defined in device-tree. The compatible string+must be: "gpio-simulator". Supported properties are:++ "gpio-sim,label" - chip label++ "gpio-sim,nr-gpios" - number of lines++Other standard GPIO properties (like "gpio-line-names" and gpio-hog) are also+supported.++Manipulating simulated lines+----------------------------++Each simulated GPIO chip creates a sysfs attribute group under its device+directory called 'line-ctrl'. Inside each group, there's a separate attribute+for each GPIO line. The name of the attribute is of the form 'gpioX' where X+is the line's offset in the chip.++Reading from a line attribute returns the current value. Writing to it (0 or 1)+changes the configuration of the simulated pull-up/pull-down resistor+(1 - pull-up, 0 - pull-down).
+ if (ret < 0) {
+ mutex_unlock(&config->lock);
+ return ret;
+ }
+
+ written += ret;
+ }
+ mutex_unlock(&config->lock);
+ return written;
+}
+
+static ssize_t gpio_sim_config_line_names_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct gpio_sim_chip_config *config = to_gpio_sim_chip_config(item);
+ unsigned int num_new_names = 1, num_old_names, name_idx = 0;
+ bool in_quote = false, got_comma = true;
+ char **new_names, **old_names, *name, c;
+ const char *start = page;
+ size_t pos, name_len;
+ int err = -EINVAL;
+
+ mutex_lock(&config->lock);
+
+ if (config->pdev) {
+ mutex_unlock(&config->lock);
+ return -EBUSY;
+ }
+
+ /*
+ * Line names are stored in a pointer array so that we can easily
+ * pass them down to the GPIO subsystem in a "gpio-line-names"
+ * property.
+ *
+ * Line names must be passed as a list of quoted names separated by
+ * commas, for example: '"foo", "bar", "foobar"'.
+ */
+
+ for (pos = 0; pos < count; pos++) {
+ /*
+ * Just count the commas and assume the number if strings
+ * equals the number of commas + 1. If the format is wrong
+ * we'll bail out anyway.
+ */
+ if (page[pos] == ',')
+ num_new_names++;
+ }
+
+ new_names = kcalloc(num_new_names, sizeof(char *), GFP_KERNEL);
+ if (!new_names) {
+ mutex_unlock(&config->lock);
+ return -ENOMEM;
+ }
+
+ /*
+ * FIXME If anyone knows a better way to parse that - please let me
+ * know.
+ */
If comma can be replaced with ' ' (space) then why not to use next_arg() from
cmdline.c? I.o.w. do you have strong opinion why should we use comma here?
+ for (pos = 0; pos < count; pos++) {
+ c = page[pos];
+
+ if (in_quote) {
+ if (c == '"') {
+ /* This is the end of the name. */
+ in_quote = got_comma = false;
+ name_len = (page + pos) - start;
+ if (name_len == 0) {
+ /* Name is empty (passed as ""). */
+ name_idx++;
+ continue;
+ }
On Thu, Mar 4, 2021 at 2:15 PM Andy Shevchenko
[off-list ref] wrote:
On Thu, Mar 04, 2021 at 11:24:49AM +0100, Bartosz Golaszewski wrote:
quoted
From: Bartosz Golaszewski <redacted>
Implement a new, modern GPIO testing module controlled by configfs
attributes instead of module parameters. The goal of this driver is
to provide a replacement for gpio-mockup that will be easily extensible
with new features and doesn't require reloading the module to change
the setup.
Shall we put a reference to this in the gpio-mockup documentation and mark the
latter deprecated?
I don't think it's necessary right away. Let's phase out gpio-mockup
once this one gets some attention (for example: after libgpiod
switches to using it).
[snip]
Yeah, so the removal of the 80 characters limit should not be abused
when there's no need for it - this doesn't look that bad really with a
broken line. Same elsewhere where the limit is exceeded.
[snip]
So this is the place where it may make sense to go over 80 chars.
[snip]
quoted
+
+ /*
+ * FIXME If anyone knows a better way to parse that - please let me
+ * know.
+ */
If comma can be replaced with ' ' (space) then why not to use next_arg() from
cmdline.c? I.o.w. do you have strong opinion why should we use comma here?
My opinion is not very strong but I wanted to make the list of names
resemble what we pass to the gpio-line-names property in device tree.
Doesn't next_arg() react differently to string of the form: "foo=bar"?
[snip]
quoted
+
+static int gpio_sim_config_uncommit_item(struct config_item *item)
+{
+ struct gpio_sim_chip_config *config = to_gpio_sim_chip_config(item);
+ int id;
+
+ mutex_lock(&config->lock);
+ id = config->pdev->id;
+ platform_device_unregister(config->pdev);
+ config->pdev = NULL;
quoted
+ ida_free(&gpio_sim_ida, id);
Isn't it atomic per se? I mean that IDA won't give the same ID until you free
it. I.o.w. why is it under the mutex?
You're right but if we rapidly create and destroy chips we'll be left
with holes in the numbering (because new devices would be created
before the IDA numbers are freed, so the driver would take a larger
number that's currently free). It doesn't hurt but it would look worse
IMO. Do you have a strong opinion on this?
[snip]
I'll address issues I didn't comment on.
Thanks for the review!
Bart
CC Greg
On Thu, Mar 4, 2021 at 11:30 AM Bartosz Golaszewski [off-list ref] wrote:
quoted hunk
From: Bartosz Golaszewski <redacted>
Export the symbol for device_is_bound() so that we can use it in gpio-sim
to check if the simulated GPIO chip is bound before fetching its driver
data from configfs callbacks in order to retrieve the name of the GPIO
chip device.
Signed-off-by: Bartosz Golaszewski <redacted>
---
drivers/base/dd.c | 1 +
1 file changed, 1 insertion(+)
On Fri, Mar 05, 2021 at 09:18:30AM +0100, Geert Uytterhoeven wrote:
CC Greg
On Thu, Mar 4, 2021 at 11:30 AM Bartosz Golaszewski [off-list ref] wrote:
quoted
From: Bartosz Golaszewski <redacted>
Export the symbol for device_is_bound() so that we can use it in gpio-sim
to check if the simulated GPIO chip is bound before fetching its driver
data from configfs callbacks in order to retrieve the name of the GPIO
chip device.
Signed-off-by: Bartosz Golaszewski <redacted>
---
drivers/base/dd.c | 1 +
1 file changed, 1 insertion(+)
On Fri, Mar 5, 2021 at 9:34 AM Greg KH [off-list ref] wrote:
On Fri, Mar 05, 2021 at 09:18:30AM +0100, Geert Uytterhoeven wrote:
quoted
CC Greg
On Thu, Mar 4, 2021 at 11:30 AM Bartosz Golaszewski [off-list ref] wrote:
quoted
From: Bartosz Golaszewski <redacted>
Export the symbol for device_is_bound() so that we can use it in gpio-sim
to check if the simulated GPIO chip is bound before fetching its driver
data from configfs callbacks in order to retrieve the name of the GPIO
chip device.
Signed-off-by: Bartosz Golaszewski <redacted>
---
drivers/base/dd.c | 1 +
1 file changed, 1 insertion(+)
No. Please no. Why is this needed? Feels like someone is doing
something really wrong...
NACK.
I should have Cc'ed you the entire series, my bad.
This is the patch that uses this change - it's a new, improved testing
module for GPIO using configfs & sysfs as you (I think) suggested a
while ago:
https://lkml.org/lkml/2021/3/4/355
The story goes like this: committing the configfs item registers a
platform device. As far as I understand - there's no guarantee that
the device will be bound to a driver before the commit callback (or
more specifically platform_device_register_full() in this case)
returns so the user may try to retrieve the name of the device
immediately (normally user-space should wait for the associated uevent
but nobody can force that) by doing:
mv /sys/kernel/config/gpio-sim/pending/foo /sys/kernel/config/gpio-sim/live/
cat /sys/kernel/config/gpio-sim/live/foo/dev_name
If the device is not bound at this point, we'll have a crash in the
kernel as opposed to just returning -ENODEV.
Please advise on how to handle it without device_is_bound().
Best Regards,
Bartosz
On Fri, Mar 05, 2021 at 09:45:41AM +0100, Bartosz Golaszewski wrote:
On Fri, Mar 5, 2021 at 9:34 AM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 09:18:30AM +0100, Geert Uytterhoeven wrote:
quoted
CC Greg
On Thu, Mar 4, 2021 at 11:30 AM Bartosz Golaszewski [off-list ref] wrote:
quoted
From: Bartosz Golaszewski <redacted>
Export the symbol for device_is_bound() so that we can use it in gpio-sim
to check if the simulated GPIO chip is bound before fetching its driver
data from configfs callbacks in order to retrieve the name of the GPIO
chip device.
Signed-off-by: Bartosz Golaszewski <redacted>
---
drivers/base/dd.c | 1 +
1 file changed, 1 insertion(+)
No. Please no. Why is this needed? Feels like someone is doing
something really wrong...
NACK.
I should have Cc'ed you the entire series, my bad.
This is the patch that uses this change - it's a new, improved testing
module for GPIO using configfs & sysfs as you (I think) suggested a
while ago:
https://lkml.org/lkml/2021/3/4/355
The story goes like this: committing the configfs item registers a
platform device.
Ick, no, stop there, that's not a "real" device, please do not abuse
platform devices like that, you all know I hate this :(
Use the virtbus code instead perhaps?
As far as I understand - there's no guarantee that
the device will be bound to a driver before the commit callback (or
more specifically platform_device_register_full() in this case)
returns so the user may try to retrieve the name of the device
immediately (normally user-space should wait for the associated uevent
but nobody can force that) by doing:
mv /sys/kernel/config/gpio-sim/pending/foo /sys/kernel/config/gpio-sim/live/
cat /sys/kernel/config/gpio-sim/live/foo/dev_name
If the device is not bound at this point, we'll have a crash in the
kernel as opposed to just returning -ENODEV.
How will the kernel crash? What has created the dev_name sysfs file
before it is possible to be read from? That feels like the root
problem.
Please advise on how to handle it without device_is_bound().
Please do not create sysfs files before they can be read from :)
thanks,
greg k-h
On Fri, Mar 5, 2021 at 9:55 AM Greg KH [off-list ref] wrote:
On Fri, Mar 05, 2021 at 09:45:41AM +0100, Bartosz Golaszewski wrote:
quoted
On Fri, Mar 5, 2021 at 9:34 AM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 09:18:30AM +0100, Geert Uytterhoeven wrote:
quoted
CC Greg
On Thu, Mar 4, 2021 at 11:30 AM Bartosz Golaszewski [off-list ref] wrote:
quoted
From: Bartosz Golaszewski <redacted>
Export the symbol for device_is_bound() so that we can use it in gpio-sim
to check if the simulated GPIO chip is bound before fetching its driver
data from configfs callbacks in order to retrieve the name of the GPIO
chip device.
Signed-off-by: Bartosz Golaszewski <redacted>
---
drivers/base/dd.c | 1 +
1 file changed, 1 insertion(+)
No. Please no. Why is this needed? Feels like someone is doing
something really wrong...
NACK.
I should have Cc'ed you the entire series, my bad.
This is the patch that uses this change - it's a new, improved testing
module for GPIO using configfs & sysfs as you (I think) suggested a
while ago:
https://lkml.org/lkml/2021/3/4/355
The story goes like this: committing the configfs item registers a
platform device.
Ick, no, stop there, that's not a "real" device, please do not abuse
platform devices like that, you all know I hate this :(
Use the virtbus code instead perhaps?
I have no idea what virtbus is and grepping for it only returns three
hits in: ./drivers/pci/iov.c and it's a function argument.
If it stands for virtual bus then for sure it sounds like the right
thing but I need to find more info on this.
quoted
As far as I understand - there's no guarantee that
the device will be bound to a driver before the commit callback (or
more specifically platform_device_register_full() in this case)
returns so the user may try to retrieve the name of the device
immediately (normally user-space should wait for the associated uevent
but nobody can force that) by doing:
mv /sys/kernel/config/gpio-sim/pending/foo /sys/kernel/config/gpio-sim/live/
cat /sys/kernel/config/gpio-sim/live/foo/dev_name
If the device is not bound at this point, we'll have a crash in the
kernel as opposed to just returning -ENODEV.
How will the kernel crash? What has created the dev_name sysfs file
before it is possible to be read from? That feels like the root
problem.
It's not sysfs - it's in configfs. Each chip has a read-only configfs
attribute that returns the name of the device - I don't really have a
better idea to map the configfs items to devices that committing
creates.
quoted
Please advise on how to handle it without device_is_bound().
Please do not create sysfs files before they can be read from :)
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Date: 2021-03-05 10:16:51
On Thu, Mar 04, 2021 at 09:15:29PM +0100, Bartosz Golaszewski wrote:
On Thu, Mar 4, 2021 at 2:15 PM Andy Shevchenko
[off-list ref] wrote:
quoted
On Thu, Mar 04, 2021 at 11:24:49AM +0100, Bartosz Golaszewski wrote:
quoted
From: Bartosz Golaszewski <redacted>
quoted
quoted
+
+ /*
+ * FIXME If anyone knows a better way to parse that - please let me
+ * know.
+ */
If comma can be replaced with ' ' (space) then why not to use next_arg() from
cmdline.c? I.o.w. do you have strong opinion why should we use comma here?
My opinion is not very strong but I wanted to make the list of names
resemble what we pass to the gpio-line-names property in device tree.
Doesn't next_arg() react differently to string of the form: "foo=bar"?
It's ambiguous here.
So, the strings '"foo=bar"' and 'foo=bar' (w/o single quotes!) are indeed
parsed differently, i.e.
'"foo=bar"' -> 'foo=bar',
while
"foo=bar" -> 'foo' + 'bar'.
...
quoted
quoted
+ ida_free(&gpio_sim_ida, id);
Isn't it atomic per se? I mean that IDA won't give the same ID until you free
it. I.o.w. why is it under the mutex?
You're right but if we rapidly create and destroy chips we'll be left
with holes in the numbering (because new devices would be created
before the IDA numbers are freed, so the driver would take a larger
number that's currently free). It doesn't hurt but it would look worse
IMO. Do you have a strong opinion on this?
It's not strong per se, but I would rather follow the 2nd rule of locking:
don't protect something which doesn't need it.
--
With Best Regards,
Andy Shevchenko
On Fri, Mar 05, 2021 at 10:16:10AM +0100, Bartosz Golaszewski wrote:
On Fri, Mar 5, 2021 at 9:55 AM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 09:45:41AM +0100, Bartosz Golaszewski wrote:
quoted
On Fri, Mar 5, 2021 at 9:34 AM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 09:18:30AM +0100, Geert Uytterhoeven wrote:
quoted
CC Greg
On Thu, Mar 4, 2021 at 11:30 AM Bartosz Golaszewski [off-list ref] wrote:
quoted
From: Bartosz Golaszewski <redacted>
Export the symbol for device_is_bound() so that we can use it in gpio-sim
to check if the simulated GPIO chip is bound before fetching its driver
data from configfs callbacks in order to retrieve the name of the GPIO
chip device.
Signed-off-by: Bartosz Golaszewski <redacted>
---
drivers/base/dd.c | 1 +
1 file changed, 1 insertion(+)
No. Please no. Why is this needed? Feels like someone is doing
something really wrong...
NACK.
I should have Cc'ed you the entire series, my bad.
This is the patch that uses this change - it's a new, improved testing
module for GPIO using configfs & sysfs as you (I think) suggested a
while ago:
https://lkml.org/lkml/2021/3/4/355
The story goes like this: committing the configfs item registers a
platform device.
Ick, no, stop there, that's not a "real" device, please do not abuse
platform devices like that, you all know I hate this :(
Use the virtbus code instead perhaps?
I have no idea what virtbus is and grepping for it only returns three
hits in: ./drivers/pci/iov.c and it's a function argument.
If it stands for virtual bus then for sure it sounds like the right
thing but I need to find more info on this.
Sorry, wrong name, see Documentation/driver-api/auxiliary_bus.rst for
the details. "virtbus" was what I think about it as that was my
original name for it, but it eventually got merged with a different
name.
quoted
quoted
As far as I understand - there's no guarantee that
the device will be bound to a driver before the commit callback (or
more specifically platform_device_register_full() in this case)
returns so the user may try to retrieve the name of the device
immediately (normally user-space should wait for the associated uevent
but nobody can force that) by doing:
mv /sys/kernel/config/gpio-sim/pending/foo /sys/kernel/config/gpio-sim/live/
cat /sys/kernel/config/gpio-sim/live/foo/dev_name
If the device is not bound at this point, we'll have a crash in the
kernel as opposed to just returning -ENODEV.
How will the kernel crash? What has created the dev_name sysfs file
before it is possible to be read from? That feels like the root
problem.
It's not sysfs - it's in configfs. Each chip has a read-only configfs
attribute that returns the name of the device - I don't really have a
better idea to map the configfs items to devices that committing
creates.
Same question, why are you exporting a configfs attribute that can not
be read from? Only export it when your driver is bound to the device.
thanks,
greg k-h
On Fri, Mar 5, 2021 at 11:24 AM Greg KH [off-list ref] wrote:
On Fri, Mar 05, 2021 at 10:16:10AM +0100, Bartosz Golaszewski wrote:
quoted
On Fri, Mar 5, 2021 at 9:55 AM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 09:45:41AM +0100, Bartosz Golaszewski wrote:
quoted
On Fri, Mar 5, 2021 at 9:34 AM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 09:18:30AM +0100, Geert Uytterhoeven wrote:
quoted
CC Greg
On Thu, Mar 4, 2021 at 11:30 AM Bartosz Golaszewski [off-list ref] wrote:
quoted
From: Bartosz Golaszewski <redacted>
Export the symbol for device_is_bound() so that we can use it in gpio-sim
to check if the simulated GPIO chip is bound before fetching its driver
data from configfs callbacks in order to retrieve the name of the GPIO
chip device.
Signed-off-by: Bartosz Golaszewski <redacted>
---
drivers/base/dd.c | 1 +
1 file changed, 1 insertion(+)
No. Please no. Why is this needed? Feels like someone is doing
something really wrong...
NACK.
I should have Cc'ed you the entire series, my bad.
This is the patch that uses this change - it's a new, improved testing
module for GPIO using configfs & sysfs as you (I think) suggested a
while ago:
https://lkml.org/lkml/2021/3/4/355
The story goes like this: committing the configfs item registers a
platform device.
Ick, no, stop there, that's not a "real" device, please do not abuse
platform devices like that, you all know I hate this :(
Use the virtbus code instead perhaps?
I have no idea what virtbus is and grepping for it only returns three
hits in: ./drivers/pci/iov.c and it's a function argument.
If it stands for virtual bus then for sure it sounds like the right
thing but I need to find more info on this.
Sorry, wrong name, see Documentation/driver-api/auxiliary_bus.rst for
the details. "virtbus" was what I think about it as that was my
original name for it, but it eventually got merged with a different
name.
quoted
quoted
quoted
As far as I understand - there's no guarantee that
the device will be bound to a driver before the commit callback (or
more specifically platform_device_register_full() in this case)
returns so the user may try to retrieve the name of the device
immediately (normally user-space should wait for the associated uevent
but nobody can force that) by doing:
mv /sys/kernel/config/gpio-sim/pending/foo /sys/kernel/config/gpio-sim/live/
cat /sys/kernel/config/gpio-sim/live/foo/dev_name
If the device is not bound at this point, we'll have a crash in the
kernel as opposed to just returning -ENODEV.
How will the kernel crash? What has created the dev_name sysfs file
before it is possible to be read from? That feels like the root
problem.
It's not sysfs - it's in configfs. Each chip has a read-only configfs
attribute that returns the name of the device - I don't really have a
better idea to map the configfs items to devices that committing
creates.
Same question, why are you exporting a configfs attribute that can not
be read from? Only export it when your driver is bound to the device.
The device doesn't know anything about configfs. Why would it? The
configuration of a GPIO chip can't be changed after it's instantiated,
this is why we have committable items.
We export a directory in configfs: gpio-sim -> user creates a new
directory (item) in gpio-sim/pending/foo and it's not tied to any
device yet but exports attributes which we use to configure the device
(label, number of lines, line names etc.), then we mv
gpio-sim/pending/foo gpio-sim/live and this is when the device gets
created and registered with the subsystem. We take all the configured
attributes and put them into device properties for both the driver and
gpiolib core (for standard properties) to read - just like we would
with a regular GPIO driver because this is the goal: test the core
code.
Configfs doesn't even allow to dynamically export and unexport attributes.
Bart
On Fri, Mar 05, 2021 at 11:58:18AM +0100, Bartosz Golaszewski wrote:
On Fri, Mar 5, 2021 at 11:24 AM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 10:16:10AM +0100, Bartosz Golaszewski wrote:
quoted
On Fri, Mar 5, 2021 at 9:55 AM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 09:45:41AM +0100, Bartosz Golaszewski wrote:
quoted
On Fri, Mar 5, 2021 at 9:34 AM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 09:18:30AM +0100, Geert Uytterhoeven wrote:
quoted
CC Greg
On Thu, Mar 4, 2021 at 11:30 AM Bartosz Golaszewski [off-list ref] wrote:
quoted
From: Bartosz Golaszewski <redacted>
Export the symbol for device_is_bound() so that we can use it in gpio-sim
to check if the simulated GPIO chip is bound before fetching its driver
data from configfs callbacks in order to retrieve the name of the GPIO
chip device.
Signed-off-by: Bartosz Golaszewski <redacted>
---
drivers/base/dd.c | 1 +
1 file changed, 1 insertion(+)
No. Please no. Why is this needed? Feels like someone is doing
something really wrong...
NACK.
I should have Cc'ed you the entire series, my bad.
This is the patch that uses this change - it's a new, improved testing
module for GPIO using configfs & sysfs as you (I think) suggested a
while ago:
https://lkml.org/lkml/2021/3/4/355
The story goes like this: committing the configfs item registers a
platform device.
Ick, no, stop there, that's not a "real" device, please do not abuse
platform devices like that, you all know I hate this :(
Use the virtbus code instead perhaps?
I have no idea what virtbus is and grepping for it only returns three
hits in: ./drivers/pci/iov.c and it's a function argument.
If it stands for virtual bus then for sure it sounds like the right
thing but I need to find more info on this.
Sorry, wrong name, see Documentation/driver-api/auxiliary_bus.rst for
the details. "virtbus" was what I think about it as that was my
original name for it, but it eventually got merged with a different
name.
quoted
quoted
quoted
As far as I understand - there's no guarantee that
the device will be bound to a driver before the commit callback (or
more specifically platform_device_register_full() in this case)
returns so the user may try to retrieve the name of the device
immediately (normally user-space should wait for the associated uevent
but nobody can force that) by doing:
mv /sys/kernel/config/gpio-sim/pending/foo /sys/kernel/config/gpio-sim/live/
cat /sys/kernel/config/gpio-sim/live/foo/dev_name
If the device is not bound at this point, we'll have a crash in the
kernel as opposed to just returning -ENODEV.
How will the kernel crash? What has created the dev_name sysfs file
before it is possible to be read from? That feels like the root
problem.
It's not sysfs - it's in configfs. Each chip has a read-only configfs
attribute that returns the name of the device - I don't really have a
better idea to map the configfs items to devices that committing
creates.
Same question, why are you exporting a configfs attribute that can not
be read from? Only export it when your driver is bound to the device.
The device doesn't know anything about configfs. Why would it? The
configuration of a GPIO chip can't be changed after it's instantiated,
this is why we have committable items.
We export a directory in configfs: gpio-sim -> user creates a new
directory (item) in gpio-sim/pending/foo and it's not tied to any
device yet but exports attributes which we use to configure the device
(label, number of lines, line names etc.), then we mv
gpio-sim/pending/foo gpio-sim/live and this is when the device gets
created and registered with the subsystem. We take all the configured
attributes and put them into device properties for both the driver and
gpiolib core (for standard properties) to read - just like we would
with a regular GPIO driver because this is the goal: test the core
code.
Ok, but they why are you trying to have dev_name be an exported thing?
I don't understand an attribute here that is visable but can not be read
from.
And why not just use the default device name function: dev_name(), which
will always return a string that will work no matter if the device is
bound to a driver or not.
thanks,
greg k-h
On Fri, Mar 5, 2021 at 12:27 PM Greg KH [off-list ref] wrote:
On Fri, Mar 05, 2021 at 11:58:18AM +0100, Bartosz Golaszewski wrote:
quoted
On Fri, Mar 5, 2021 at 11:24 AM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 10:16:10AM +0100, Bartosz Golaszewski wrote:
quoted
On Fri, Mar 5, 2021 at 9:55 AM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 09:45:41AM +0100, Bartosz Golaszewski wrote:
quoted
On Fri, Mar 5, 2021 at 9:34 AM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 09:18:30AM +0100, Geert Uytterhoeven wrote:
quoted
CC Greg
On Thu, Mar 4, 2021 at 11:30 AM Bartosz Golaszewski [off-list ref] wrote:
quoted
From: Bartosz Golaszewski <redacted>
Export the symbol for device_is_bound() so that we can use it in gpio-sim
to check if the simulated GPIO chip is bound before fetching its driver
data from configfs callbacks in order to retrieve the name of the GPIO
chip device.
Signed-off-by: Bartosz Golaszewski <redacted>
---
drivers/base/dd.c | 1 +
1 file changed, 1 insertion(+)
No. Please no. Why is this needed? Feels like someone is doing
something really wrong...
NACK.
I should have Cc'ed you the entire series, my bad.
This is the patch that uses this change - it's a new, improved testing
module for GPIO using configfs & sysfs as you (I think) suggested a
while ago:
https://lkml.org/lkml/2021/3/4/355
The story goes like this: committing the configfs item registers a
platform device.
Ick, no, stop there, that's not a "real" device, please do not abuse
platform devices like that, you all know I hate this :(
Use the virtbus code instead perhaps?
I have no idea what virtbus is and grepping for it only returns three
hits in: ./drivers/pci/iov.c and it's a function argument.
If it stands for virtual bus then for sure it sounds like the right
thing but I need to find more info on this.
Sorry, wrong name, see Documentation/driver-api/auxiliary_bus.rst for
the details. "virtbus" was what I think about it as that was my
original name for it, but it eventually got merged with a different
name.
Unless I'm not seeing something - it completely doesn't look like the
right solution. This auxiliary bus sounds like MFD with extra steps.
Its aim seems to be to provide virtual devices for sub-modules of real
devices.
What I have here really is a dummy device for which no HW exists.
Also: while the preferred way is to use configfs to instantiate these
simulated devices, then can still be registered from device-tree (this
is a feature that was requested and eventually implemented in
gpio-mockup which we want to phase out so we can't just drop it).
AFAIK only platform devices can be populated from DT.
I guess we could create something like a "virtual bus" that would be
there for devices that don't exist on any physical bus but this would
end up in big part being the same thing as platform devices.
quoted
quoted
quoted
quoted
quoted
As far as I understand - there's no guarantee that
the device will be bound to a driver before the commit callback (or
more specifically platform_device_register_full() in this case)
returns so the user may try to retrieve the name of the device
immediately (normally user-space should wait for the associated uevent
but nobody can force that) by doing:
mv /sys/kernel/config/gpio-sim/pending/foo /sys/kernel/config/gpio-sim/live/
cat /sys/kernel/config/gpio-sim/live/foo/dev_name
If the device is not bound at this point, we'll have a crash in the
kernel as opposed to just returning -ENODEV.
How will the kernel crash? What has created the dev_name sysfs file
before it is possible to be read from? That feels like the root
problem.
It's not sysfs - it's in configfs. Each chip has a read-only configfs
attribute that returns the name of the device - I don't really have a
better idea to map the configfs items to devices that committing
creates.
Same question, why are you exporting a configfs attribute that can not
be read from? Only export it when your driver is bound to the device.
The device doesn't know anything about configfs. Why would it? The
configuration of a GPIO chip can't be changed after it's instantiated,
this is why we have committable items.
We export a directory in configfs: gpio-sim -> user creates a new
directory (item) in gpio-sim/pending/foo and it's not tied to any
device yet but exports attributes which we use to configure the device
(label, number of lines, line names etc.), then we mv
gpio-sim/pending/foo gpio-sim/live and this is when the device gets
created and registered with the subsystem. We take all the configured
attributes and put them into device properties for both the driver and
gpiolib core (for standard properties) to read - just like we would
with a regular GPIO driver because this is the goal: test the core
code.
Ok, but they why are you trying to have dev_name be an exported thing?
I don't understand an attribute here that is visable but can not be read
from.
Because once the associated configfs item is committed and the device
created, it will become readable. The list of attributes is fixed in
configfs. I'm not sure what the better approach would be - return
"none" if the device handle is NULL?
And why not just use the default device name function: dev_name(), which
will always return a string that will work no matter if the device is
bound to a driver or not.
I can do this but then it's possible that user-space gets the name of
the device which doesn't exist in sysfs. I guess we can mention that
in the documentation.
Bartosz
On Fri, Mar 05, 2021 at 03:20:27PM +0100, Bartosz Golaszewski wrote:
On Fri, Mar 5, 2021 at 12:27 PM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 11:58:18AM +0100, Bartosz Golaszewski wrote:
quoted
On Fri, Mar 5, 2021 at 11:24 AM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 10:16:10AM +0100, Bartosz Golaszewski wrote:
quoted
On Fri, Mar 5, 2021 at 9:55 AM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 09:45:41AM +0100, Bartosz Golaszewski wrote:
quoted
On Fri, Mar 5, 2021 at 9:34 AM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 09:18:30AM +0100, Geert Uytterhoeven wrote:
quoted
CC Greg
On Thu, Mar 4, 2021 at 11:30 AM Bartosz Golaszewski [off-list ref] wrote:
quoted
From: Bartosz Golaszewski <redacted>
Export the symbol for device_is_bound() so that we can use it in gpio-sim
to check if the simulated GPIO chip is bound before fetching its driver
data from configfs callbacks in order to retrieve the name of the GPIO
chip device.
Signed-off-by: Bartosz Golaszewski <redacted>
---
drivers/base/dd.c | 1 +
1 file changed, 1 insertion(+)
No. Please no. Why is this needed? Feels like someone is doing
something really wrong...
NACK.
I should have Cc'ed you the entire series, my bad.
This is the patch that uses this change - it's a new, improved testing
module for GPIO using configfs & sysfs as you (I think) suggested a
while ago:
https://lkml.org/lkml/2021/3/4/355
The story goes like this: committing the configfs item registers a
platform device.
Ick, no, stop there, that's not a "real" device, please do not abuse
platform devices like that, you all know I hate this :(
Use the virtbus code instead perhaps?
I have no idea what virtbus is and grepping for it only returns three
hits in: ./drivers/pci/iov.c and it's a function argument.
If it stands for virtual bus then for sure it sounds like the right
thing but I need to find more info on this.
Sorry, wrong name, see Documentation/driver-api/auxiliary_bus.rst for
the details. "virtbus" was what I think about it as that was my
original name for it, but it eventually got merged with a different
name.
Unless I'm not seeing something - it completely doesn't look like the
right solution. This auxiliary bus sounds like MFD with extra steps.
Its aim seems to be to provide virtual devices for sub-modules of real
devices.
What I have here really is a dummy device for which no HW exists.
Then just use a "normal" virtual device. We have loads of them. But if
you want to bind a "driver" to it, then use the aux bus please. Do NOT
abuse a platform device for this.
Also: while the preferred way is to use configfs to instantiate these
simulated devices, then can still be registered from device-tree (this
is a feature that was requested and eventually implemented in
gpio-mockup which we want to phase out so we can't just drop it).
AFAIK only platform devices can be populated from DT.
If you really are using DT, then ok, a platform device can be used, but
you didn't say that :)
I guess we could create something like a "virtual bus" that would be
there for devices that don't exist on any physical bus but this would
end up in big part being the same thing as platform devices.
That's what the aux bus code is there for. So maybe you do need to use
it.
quoted
quoted
quoted
quoted
quoted
quoted
As far as I understand - there's no guarantee that
the device will be bound to a driver before the commit callback (or
more specifically platform_device_register_full() in this case)
returns so the user may try to retrieve the name of the device
immediately (normally user-space should wait for the associated uevent
but nobody can force that) by doing:
mv /sys/kernel/config/gpio-sim/pending/foo /sys/kernel/config/gpio-sim/live/
cat /sys/kernel/config/gpio-sim/live/foo/dev_name
If the device is not bound at this point, we'll have a crash in the
kernel as opposed to just returning -ENODEV.
How will the kernel crash? What has created the dev_name sysfs file
before it is possible to be read from? That feels like the root
problem.
It's not sysfs - it's in configfs. Each chip has a read-only configfs
attribute that returns the name of the device - I don't really have a
better idea to map the configfs items to devices that committing
creates.
Same question, why are you exporting a configfs attribute that can not
be read from? Only export it when your driver is bound to the device.
The device doesn't know anything about configfs. Why would it? The
configuration of a GPIO chip can't be changed after it's instantiated,
this is why we have committable items.
We export a directory in configfs: gpio-sim -> user creates a new
directory (item) in gpio-sim/pending/foo and it's not tied to any
device yet but exports attributes which we use to configure the device
(label, number of lines, line names etc.), then we mv
gpio-sim/pending/foo gpio-sim/live and this is when the device gets
created and registered with the subsystem. We take all the configured
attributes and put them into device properties for both the driver and
gpiolib core (for standard properties) to read - just like we would
with a regular GPIO driver because this is the goal: test the core
code.
Ok, but they why are you trying to have dev_name be an exported thing?
I don't understand an attribute here that is visable but can not be read
from.
Because once the associated configfs item is committed and the device
created, it will become readable. The list of attributes is fixed in
configfs. I'm not sure what the better approach would be - return
"none" if the device handle is NULL?
Sounds reasonable, I don't know how configfs works, it's been a decade
since I last touched it.
quoted
And why not just use the default device name function: dev_name(), which
will always return a string that will work no matter if the device is
bound to a driver or not.
I can do this but then it's possible that user-space gets the name of
the device which doesn't exist in sysfs. I guess we can mention that
in the documentation.
Device names can change over time, nothing new there.
thanks,
greg k-h
On Fri, Mar 5, 2021 at 4:01 PM Greg KH [off-list ref] wrote:
On Fri, Mar 05, 2021 at 03:20:27PM +0100, Bartosz Golaszewski wrote:
quoted
On Fri, Mar 5, 2021 at 12:27 PM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 11:58:18AM +0100, Bartosz Golaszewski wrote:
quoted
On Fri, Mar 5, 2021 at 11:24 AM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 10:16:10AM +0100, Bartosz Golaszewski wrote:
quoted
On Fri, Mar 5, 2021 at 9:55 AM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 09:45:41AM +0100, Bartosz Golaszewski wrote:
quoted
On Fri, Mar 5, 2021 at 9:34 AM Greg KH [off-list ref] wrote:
quoted
On Fri, Mar 05, 2021 at 09:18:30AM +0100, Geert Uytterhoeven wrote:
quoted
CC Greg
On Thu, Mar 4, 2021 at 11:30 AM Bartosz Golaszewski [off-list ref] wrote:
quoted
From: Bartosz Golaszewski <redacted>
Export the symbol for device_is_bound() so that we can use it in gpio-sim
to check if the simulated GPIO chip is bound before fetching its driver
data from configfs callbacks in order to retrieve the name of the GPIO
chip device.
Signed-off-by: Bartosz Golaszewski <redacted>
---
drivers/base/dd.c | 1 +
1 file changed, 1 insertion(+)
No. Please no. Why is this needed? Feels like someone is doing
something really wrong...
NACK.
I should have Cc'ed you the entire series, my bad.
This is the patch that uses this change - it's a new, improved testing
module for GPIO using configfs & sysfs as you (I think) suggested a
while ago:
https://lkml.org/lkml/2021/3/4/355
The story goes like this: committing the configfs item registers a
platform device.
Ick, no, stop there, that's not a "real" device, please do not abuse
platform devices like that, you all know I hate this :(
Use the virtbus code instead perhaps?
I have no idea what virtbus is and grepping for it only returns three
hits in: ./drivers/pci/iov.c and it's a function argument.
If it stands for virtual bus then for sure it sounds like the right
thing but I need to find more info on this.
Sorry, wrong name, see Documentation/driver-api/auxiliary_bus.rst for
the details. "virtbus" was what I think about it as that was my
original name for it, but it eventually got merged with a different
name.
Unless I'm not seeing something - it completely doesn't look like the
right solution. This auxiliary bus sounds like MFD with extra steps.
Its aim seems to be to provide virtual devices for sub-modules of real
devices.
What I have here really is a dummy device for which no HW exists.
Then just use a "normal" virtual device. We have loads of them. But if
you want to bind a "driver" to it, then use the aux bus please. Do NOT
abuse a platform device for this.
quoted
Also: while the preferred way is to use configfs to instantiate these
simulated devices, then can still be registered from device-tree (this
is a feature that was requested and eventually implemented in
gpio-mockup which we want to phase out so we can't just drop it).
AFAIK only platform devices can be populated from DT.
If you really are using DT, then ok, a platform device can be used, but
you didn't say that :)
My bad. Yes we need to use DT. And platform device does sound like the
best approach.
quoted
I guess we could create something like a "virtual bus" that would be
there for devices that don't exist on any physical bus but this would
end up in big part being the same thing as platform devices.
That's what the aux bus code is there for. So maybe you do need to use
it.
I'm fine with that if it can be instantiated from DT but it doesn't seem so.
quoted
quoted
quoted
quoted
quoted
quoted
quoted
As far as I understand - there's no guarantee that
the device will be bound to a driver before the commit callback (or
more specifically platform_device_register_full() in this case)
returns so the user may try to retrieve the name of the device
immediately (normally user-space should wait for the associated uevent
but nobody can force that) by doing:
mv /sys/kernel/config/gpio-sim/pending/foo /sys/kernel/config/gpio-sim/live/
cat /sys/kernel/config/gpio-sim/live/foo/dev_name
If the device is not bound at this point, we'll have a crash in the
kernel as opposed to just returning -ENODEV.
How will the kernel crash? What has created the dev_name sysfs file
before it is possible to be read from? That feels like the root
problem.
It's not sysfs - it's in configfs. Each chip has a read-only configfs
attribute that returns the name of the device - I don't really have a
better idea to map the configfs items to devices that committing
creates.
Same question, why are you exporting a configfs attribute that can not
be read from? Only export it when your driver is bound to the device.
The device doesn't know anything about configfs. Why would it? The
configuration of a GPIO chip can't be changed after it's instantiated,
this is why we have committable items.
We export a directory in configfs: gpio-sim -> user creates a new
directory (item) in gpio-sim/pending/foo and it's not tied to any
device yet but exports attributes which we use to configure the device
(label, number of lines, line names etc.), then we mv
gpio-sim/pending/foo gpio-sim/live and this is when the device gets
created and registered with the subsystem. We take all the configured
attributes and put them into device properties for both the driver and
gpiolib core (for standard properties) to read - just like we would
with a regular GPIO driver because this is the goal: test the core
code.
Ok, but they why are you trying to have dev_name be an exported thing?
I don't understand an attribute here that is visable but can not be read
from.
Because once the associated configfs item is committed and the device
created, it will become readable. The list of attributes is fixed in
configfs. I'm not sure what the better approach would be - return
"none" if the device handle is NULL?
Sounds reasonable, I don't know how configfs works, it's been a decade
since I last touched it.
quoted
quoted
And why not just use the default device name function: dev_name(), which
will always return a string that will work no matter if the device is
bound to a driver or not.
I can do this but then it's possible that user-space gets the name of
the device which doesn't exist in sysfs. I guess we can mention that
in the documentation.
Device names can change over time, nothing new there.
On Fri, Mar 5, 2021 at 11:15 AM Andy Shevchenko
[off-list ref] wrote:
On Thu, Mar 04, 2021 at 09:15:29PM +0100, Bartosz Golaszewski wrote:
quoted
On Thu, Mar 4, 2021 at 2:15 PM Andy Shevchenko
[off-list ref] wrote:
quoted
On Thu, Mar 04, 2021 at 11:24:49AM +0100, Bartosz Golaszewski wrote:
quoted
From: Bartosz Golaszewski <redacted>
quoted
quoted
quoted
+
+ /*
+ * FIXME If anyone knows a better way to parse that - please let me
+ * know.
+ */
If comma can be replaced with ' ' (space) then why not to use next_arg() from
cmdline.c? I.o.w. do you have strong opinion why should we use comma here?
My opinion is not very strong but I wanted to make the list of names
resemble what we pass to the gpio-line-names property in device tree.
Doesn't next_arg() react differently to string of the form: "foo=bar"?
It's ambiguous here.
So, the strings '"foo=bar"' and 'foo=bar' (w/o single quotes!) are indeed
parsed differently, i.e.
'"foo=bar"' -> 'foo=bar',
while
"foo=bar" -> 'foo' + 'bar'.
IMO '"foo", "bar", "", "foobar"' looks better than '"foo" "bar" ""
"foobar"' and I'm also not sure next_arg will understand an empty
quote?
If you're not objecting strongly, then I would prefer my version.
...
quoted
quoted
quoted
+ ida_free(&gpio_sim_ida, id);
Isn't it atomic per se? I mean that IDA won't give the same ID until you free
it. I.o.w. why is it under the mutex?
You're right but if we rapidly create and destroy chips we'll be left
with holes in the numbering (because new devices would be created
before the IDA numbers are freed, so the driver would take a larger
number that's currently free). It doesn't hurt but it would look worse
IMO. Do you have a strong opinion on this?
It's not strong per se, but I would rather follow the 2nd rule of locking:
don't protect something which doesn't need it.
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Date: 2021-03-08 15:05:33
On Mon, Mar 08, 2021 at 03:23:31PM +0100, Bartosz Golaszewski wrote:
On Fri, Mar 5, 2021 at 11:15 AM Andy Shevchenko
[off-list ref] wrote:
quoted
On Thu, Mar 04, 2021 at 09:15:29PM +0100, Bartosz Golaszewski wrote:
quoted
On Thu, Mar 4, 2021 at 2:15 PM Andy Shevchenko
[off-list ref] wrote:
quoted
On Thu, Mar 04, 2021 at 11:24:49AM +0100, Bartosz Golaszewski wrote:
quoted
From: Bartosz Golaszewski <redacted>
quoted
quoted
quoted
quoted
+
+ /*
+ * FIXME If anyone knows a better way to parse that - please let me
+ * know.
+ */
If comma can be replaced with ' ' (space) then why not to use next_arg() from
cmdline.c? I.o.w. do you have strong opinion why should we use comma here?
My opinion is not very strong but I wanted to make the list of names
resemble what we pass to the gpio-line-names property in device tree.
Doesn't next_arg() react differently to string of the form: "foo=bar"?
It's ambiguous here.
So, the strings '"foo=bar"' and 'foo=bar' (w/o single quotes!) are indeed
parsed differently, i.e.
'"foo=bar"' -> 'foo=bar',
while
"foo=bar" -> 'foo' + 'bar'.
IMO '"foo", "bar", "", "foobar"' looks better than '"foo" "bar" ""
"foobar"' and I'm also not sure next_arg will understand an empty
quote?
I guess it understands it. But I agree that comma-separated it would look
better.
If you're not objecting strongly, then I would prefer my version.
I have strong opinion not to open code "yet another parser".
So, grepping on 'strsep(.*, ",")' shows a lot of code that wants something like
this. Interesting are the net/9p cases. This in particular pointed out to
lib/parser.c which in turn shows promising match_strlcpy() / match_strdup(). I
haven't looked deeply though.
That said, I agree that next_arg() is not the best here.
--
With Best Regards,
Andy Shevchenko
On Mon, Mar 8, 2021 at 4:05 PM Andy Shevchenko
[off-list ref] wrote:
On Mon, Mar 08, 2021 at 03:23:31PM +0100, Bartosz Golaszewski wrote:
quoted
On Fri, Mar 5, 2021 at 11:15 AM Andy Shevchenko
[off-list ref] wrote:
quoted
On Thu, Mar 04, 2021 at 09:15:29PM +0100, Bartosz Golaszewski wrote:
quoted
On Thu, Mar 4, 2021 at 2:15 PM Andy Shevchenko
[off-list ref] wrote:
quoted
On Thu, Mar 04, 2021 at 11:24:49AM +0100, Bartosz Golaszewski wrote:
quoted
From: Bartosz Golaszewski <redacted>
quoted
quoted
quoted
quoted
quoted
+
+ /*
+ * FIXME If anyone knows a better way to parse that - please let me
+ * know.
+ */
If comma can be replaced with ' ' (space) then why not to use next_arg() from
cmdline.c? I.o.w. do you have strong opinion why should we use comma here?
My opinion is not very strong but I wanted to make the list of names
resemble what we pass to the gpio-line-names property in device tree.
Doesn't next_arg() react differently to string of the form: "foo=bar"?
It's ambiguous here.
So, the strings '"foo=bar"' and 'foo=bar' (w/o single quotes!) are indeed
parsed differently, i.e.
'"foo=bar"' -> 'foo=bar',
while
"foo=bar" -> 'foo' + 'bar'.
IMO '"foo", "bar", "", "foobar"' looks better than '"foo" "bar" ""
"foobar"' and I'm also not sure next_arg will understand an empty
quote?
I guess it understands it. But I agree that comma-separated it would look
better.
quoted
If you're not objecting strongly, then I would prefer my version.
I have strong opinion not to open code "yet another parser".
So, grepping on 'strsep(.*, ",")' shows a lot of code that wants something like
this. Interesting are the net/9p cases. This in particular pointed out to
lib/parser.c which in turn shows promising match_strlcpy() / match_strdup(). I
haven't looked deeply though.
That said, I agree that next_arg() is not the best here.
--
With Best Regards,
Andy Shevchenko
Shall we revisit this once it's upstream with a generalization for
separating comma separated strings?
Bart
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Date: 2021-03-08 15:33:34
On Mon, Mar 08, 2021 at 04:13:33PM +0100, Bartosz Golaszewski wrote:
On Mon, Mar 8, 2021 at 4:05 PM Andy Shevchenko
[off-list ref] wrote:
quoted
On Mon, Mar 08, 2021 at 03:23:31PM +0100, Bartosz Golaszewski wrote:
...
quoted
I have strong opinion not to open code "yet another parser".
So, grepping on 'strsep(.*, ",")' shows a lot of code that wants something like
this. Interesting are the net/9p cases. This in particular pointed out to
lib/parser.c which in turn shows promising match_strlcpy() / match_strdup(). I
haven't looked deeply though.
That said, I agree that next_arg() is not the best here.
Shall we revisit this once it's upstream with a generalization for
separating comma separated strings?
How can we guarantee it won't be forgotten?
--
With Best Regards,
Andy Shevchenko
On Mon, Mar 8, 2021 at 4:32 PM Andy Shevchenko
[off-list ref] wrote:
On Mon, Mar 08, 2021 at 04:13:33PM +0100, Bartosz Golaszewski wrote:
quoted
On Mon, Mar 8, 2021 at 4:05 PM Andy Shevchenko
[off-list ref] wrote:
quoted
On Mon, Mar 08, 2021 at 03:23:31PM +0100, Bartosz Golaszewski wrote:
...
quoted
quoted
I have strong opinion not to open code "yet another parser".
So, grepping on 'strsep(.*, ",")' shows a lot of code that wants something like
this. Interesting are the net/9p cases. This in particular pointed out to
lib/parser.c which in turn shows promising match_strlcpy() / match_strdup(). I
haven't looked deeply though.
That said, I agree that next_arg() is not the best here.
Shall we revisit this once it's upstream with a generalization for
separating comma separated strings?
How can we guarantee it won't be forgotten?
I will add a REVISIT comment, so *obviously* it ***will*** be revisited. :)
Bartosz
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Date: 2021-03-08 16:38:08
On Mon, Mar 08, 2021 at 04:37:10PM +0100, Bartosz Golaszewski wrote:
On Mon, Mar 8, 2021 at 4:32 PM Andy Shevchenko
[off-list ref] wrote:
quoted
On Mon, Mar 08, 2021 at 04:13:33PM +0100, Bartosz Golaszewski wrote:
quoted
On Mon, Mar 8, 2021 at 4:05 PM Andy Shevchenko
[off-list ref] wrote:
quoted
On Mon, Mar 08, 2021 at 03:23:31PM +0100, Bartosz Golaszewski wrote:
...
quoted
quoted
I have strong opinion not to open code "yet another parser".
So, grepping on 'strsep(.*, ",")' shows a lot of code that wants something like
this. Interesting are the net/9p cases. This in particular pointed out to
lib/parser.c which in turn shows promising match_strlcpy() / match_strdup(). I
haven't looked deeply though.
That said, I agree that next_arg() is not the best here.
Shall we revisit this once it's upstream with a generalization for
separating comma separated strings?
How can we guarantee it won't be forgotten?
I will add a REVISIT comment, so *obviously* it ***will*** be revisited. :)
On Thu, Mar 4, 2021 at 1:59 PM Andy Shevchenko
[off-list ref] wrote:
On Thu, Mar 04, 2021 at 11:24:45AM +0100, Bartosz Golaszewski wrote:
quoted
From: Bartosz Golaszewski <redacted>
The 'extern' keyword doesn't have any benefits in header files. Remove it.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
A few nitpicks below.
quoted
Signed-off-by: Bartosz Golaszewski <redacted>
---
Hi Andy,
regarding this patch and other places where you raise issues with line
breaking: I believe this is purely a question of taste. There are no
guidelines on line breaking in the docs. I will leave it as it is here
because it's not better or worse than your version, just different.
Same for exceeding 80 characters - I personally believe it's justified
when the line looks better but whenever it can be cleanly broken, it's
better to stay within the limit.
Best Regards,
Bartosz