Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Generic Counter sysfs interface
   4 * Copyright (C) 2020 William Breathitt Gray
   5 */
   6#include <linux/counter.h>
   7#include <linux/device.h>
   8#include <linux/err.h>
   9#include <linux/gfp.h>
  10#include <linux/kernel.h>
  11#include <linux/kfifo.h>
  12#include <linux/kstrtox.h>
  13#include <linux/list.h>
  14#include <linux/mutex.h>
  15#include <linux/spinlock.h>
  16#include <linux/string.h>
  17#include <linux/sysfs.h>
  18#include <linux/types.h>
  19
  20#include "counter-sysfs.h"
  21
  22static inline struct counter_device *counter_from_dev(struct device *dev)
  23{
  24	return container_of(dev, struct counter_device, dev);
  25}
  26
  27/**
  28 * struct counter_attribute - Counter sysfs attribute
  29 * @dev_attr:	device attribute for sysfs
  30 * @l:		node to add Counter attribute to attribute group list
  31 * @comp:	Counter component callbacks and data
  32 * @scope:	Counter scope of the attribute
  33 * @parent:	pointer to the parent component
  34 */
  35struct counter_attribute {
  36	struct device_attribute dev_attr;
  37	struct list_head l;
  38
  39	struct counter_comp comp;
  40	enum counter_scope scope;
  41	void *parent;
  42};
  43
  44#define to_counter_attribute(_dev_attr) \
  45	container_of(_dev_attr, struct counter_attribute, dev_attr)
  46
  47/**
  48 * struct counter_attribute_group - container for attribute group
  49 * @name:	name of the attribute group
  50 * @attr_list:	list to keep track of created attributes
  51 * @num_attr:	number of attributes
  52 */
  53struct counter_attribute_group {
  54	const char *name;
  55	struct list_head attr_list;
  56	size_t num_attr;
  57};
  58
  59static const char *const counter_function_str[] = {
  60	[COUNTER_FUNCTION_INCREASE] = "increase",
  61	[COUNTER_FUNCTION_DECREASE] = "decrease",
  62	[COUNTER_FUNCTION_PULSE_DIRECTION] = "pulse-direction",
  63	[COUNTER_FUNCTION_QUADRATURE_X1_A] = "quadrature x1 a",
  64	[COUNTER_FUNCTION_QUADRATURE_X1_B] = "quadrature x1 b",
  65	[COUNTER_FUNCTION_QUADRATURE_X2_A] = "quadrature x2 a",
  66	[COUNTER_FUNCTION_QUADRATURE_X2_B] = "quadrature x2 b",
  67	[COUNTER_FUNCTION_QUADRATURE_X4] = "quadrature x4"
  68};
  69
  70static const char *const counter_signal_value_str[] = {
  71	[COUNTER_SIGNAL_LEVEL_LOW] = "low",
  72	[COUNTER_SIGNAL_LEVEL_HIGH] = "high"
  73};
  74
  75static const char *const counter_synapse_action_str[] = {
  76	[COUNTER_SYNAPSE_ACTION_NONE] = "none",
  77	[COUNTER_SYNAPSE_ACTION_RISING_EDGE] = "rising edge",
  78	[COUNTER_SYNAPSE_ACTION_FALLING_EDGE] = "falling edge",
  79	[COUNTER_SYNAPSE_ACTION_BOTH_EDGES] = "both edges"
  80};
  81
  82static const char *const counter_count_direction_str[] = {
  83	[COUNTER_COUNT_DIRECTION_FORWARD] = "forward",
  84	[COUNTER_COUNT_DIRECTION_BACKWARD] = "backward"
  85};
  86
  87static const char *const counter_count_mode_str[] = {
  88	[COUNTER_COUNT_MODE_NORMAL] = "normal",
  89	[COUNTER_COUNT_MODE_RANGE_LIMIT] = "range limit",
  90	[COUNTER_COUNT_MODE_NON_RECYCLE] = "non-recycle",
  91	[COUNTER_COUNT_MODE_MODULO_N] = "modulo-n"
  92};
  93
  94static const char *const counter_signal_polarity_str[] = {
  95	[COUNTER_SIGNAL_POLARITY_POSITIVE] = "positive",
  96	[COUNTER_SIGNAL_POLARITY_NEGATIVE] = "negative"
  97};
  98
  99static ssize_t counter_comp_u8_show(struct device *dev,
 100				    struct device_attribute *attr, char *buf)
 101{
 102	const struct counter_attribute *const a = to_counter_attribute(attr);
 103	struct counter_device *const counter = counter_from_dev(dev);
 104	int err;
 105	u8 data = 0;
 106
 107	switch (a->scope) {
 108	case COUNTER_SCOPE_DEVICE:
 109		err = a->comp.device_u8_read(counter, &data);
 110		break;
 111	case COUNTER_SCOPE_SIGNAL:
 112		err = a->comp.signal_u8_read(counter, a->parent, &data);
 113		break;
 114	case COUNTER_SCOPE_COUNT:
 115		err = a->comp.count_u8_read(counter, a->parent, &data);
 116		break;
 117	default:
 118		return -EINVAL;
 119	}
 120	if (err < 0)
 121		return err;
 122
 123	if (a->comp.type == COUNTER_COMP_BOOL)
 124		/* data should already be boolean but ensure just to be safe */
 125		data = !!data;
 126
 127	return sysfs_emit(buf, "%u\n", (unsigned int)data);
 128}
 129
 130static ssize_t counter_comp_u8_store(struct device *dev,
 131				     struct device_attribute *attr,
 132				     const char *buf, size_t len)
 133{
 134	const struct counter_attribute *const a = to_counter_attribute(attr);
 135	struct counter_device *const counter = counter_from_dev(dev);
 136	int err;
 137	bool bool_data = 0;
 138	u8 data = 0;
 139
 140	if (a->comp.type == COUNTER_COMP_BOOL) {
 141		err = kstrtobool(buf, &bool_data);
 142		data = bool_data;
 143	} else
 144		err = kstrtou8(buf, 0, &data);
 145	if (err < 0)
 146		return err;
 147
 148	switch (a->scope) {
 149	case COUNTER_SCOPE_DEVICE:
 150		err = a->comp.device_u8_write(counter, data);
 151		break;
 152	case COUNTER_SCOPE_SIGNAL:
 153		err = a->comp.signal_u8_write(counter, a->parent, data);
 154		break;
 155	case COUNTER_SCOPE_COUNT:
 156		err = a->comp.count_u8_write(counter, a->parent, data);
 157		break;
 158	default:
 159		return -EINVAL;
 160	}
 161	if (err < 0)
 162		return err;
 163
 164	return len;
 165}
 166
 167static ssize_t counter_comp_u32_show(struct device *dev,
 168				     struct device_attribute *attr, char *buf)
 169{
 170	const struct counter_attribute *const a = to_counter_attribute(attr);
 171	struct counter_device *const counter = counter_from_dev(dev);
 172	const struct counter_available *const avail = a->comp.priv;
 173	int err;
 174	u32 data = 0;
 175
 176	switch (a->scope) {
 177	case COUNTER_SCOPE_DEVICE:
 178		err = a->comp.device_u32_read(counter, &data);
 179		break;
 180	case COUNTER_SCOPE_SIGNAL:
 181		err = a->comp.signal_u32_read(counter, a->parent, &data);
 182		break;
 183	case COUNTER_SCOPE_COUNT:
 184		if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION)
 185			err = a->comp.action_read(counter, a->parent,
 186						  a->comp.priv, &data);
 187		else
 188			err = a->comp.count_u32_read(counter, a->parent, &data);
 189		break;
 190	default:
 191		return -EINVAL;
 192	}
 193	if (err < 0)
 194		return err;
 195
 196	switch (a->comp.type) {
 197	case COUNTER_COMP_FUNCTION:
 198		return sysfs_emit(buf, "%s\n", counter_function_str[data]);
 199	case COUNTER_COMP_SIGNAL_LEVEL:
 200		return sysfs_emit(buf, "%s\n", counter_signal_value_str[data]);
 201	case COUNTER_COMP_SYNAPSE_ACTION:
 202		return sysfs_emit(buf, "%s\n", counter_synapse_action_str[data]);
 203	case COUNTER_COMP_ENUM:
 204		return sysfs_emit(buf, "%s\n", avail->strs[data]);
 205	case COUNTER_COMP_COUNT_DIRECTION:
 206		return sysfs_emit(buf, "%s\n", counter_count_direction_str[data]);
 207	case COUNTER_COMP_COUNT_MODE:
 208		return sysfs_emit(buf, "%s\n", counter_count_mode_str[data]);
 209	case COUNTER_COMP_SIGNAL_POLARITY:
 210		return sysfs_emit(buf, "%s\n", counter_signal_polarity_str[data]);
 211	default:
 212		return sysfs_emit(buf, "%u\n", (unsigned int)data);
 213	}
 214}
 215
 216static int counter_find_enum(u32 *const enum_item, const u32 *const enums,
 217			     const size_t num_enums, const char *const buf,
 218			     const char *const string_array[])
 219{
 220	size_t index;
 221
 222	for (index = 0; index < num_enums; index++) {
 223		*enum_item = enums[index];
 224		if (sysfs_streq(buf, string_array[*enum_item]))
 225			return 0;
 226	}
 227
 228	return -EINVAL;
 229}
 230
 231static ssize_t counter_comp_u32_store(struct device *dev,
 232				      struct device_attribute *attr,
 233				      const char *buf, size_t len)
 234{
 235	const struct counter_attribute *const a = to_counter_attribute(attr);
 236	struct counter_device *const counter = counter_from_dev(dev);
 237	struct counter_count *const count = a->parent;
 238	struct counter_synapse *const synapse = a->comp.priv;
 239	const struct counter_available *const avail = a->comp.priv;
 240	int err;
 241	u32 data = 0;
 242
 243	switch (a->comp.type) {
 244	case COUNTER_COMP_FUNCTION:
 245		err = counter_find_enum(&data, count->functions_list,
 246					count->num_functions, buf,
 247					counter_function_str);
 248		break;
 249	case COUNTER_COMP_SYNAPSE_ACTION:
 250		err = counter_find_enum(&data, synapse->actions_list,
 251					synapse->num_actions, buf,
 252					counter_synapse_action_str);
 253		break;
 254	case COUNTER_COMP_ENUM:
 255		err = __sysfs_match_string(avail->strs, avail->num_items, buf);
 256		data = err;
 257		break;
 258	case COUNTER_COMP_COUNT_MODE:
 259		err = counter_find_enum(&data, avail->enums, avail->num_items,
 260					buf, counter_count_mode_str);
 261		break;
 262	case COUNTER_COMP_SIGNAL_POLARITY:
 263		err = counter_find_enum(&data, avail->enums, avail->num_items,
 264					buf, counter_signal_polarity_str);
 265		break;
 266	default:
 267		err = kstrtou32(buf, 0, &data);
 268		break;
 269	}
 270	if (err < 0)
 271		return err;
 272
 273	switch (a->scope) {
 274	case COUNTER_SCOPE_DEVICE:
 275		err = a->comp.device_u32_write(counter, data);
 276		break;
 277	case COUNTER_SCOPE_SIGNAL:
 278		err = a->comp.signal_u32_write(counter, a->parent, data);
 279		break;
 280	case COUNTER_SCOPE_COUNT:
 281		if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION)
 282			err = a->comp.action_write(counter, count, synapse,
 283						   data);
 284		else
 285			err = a->comp.count_u32_write(counter, count, data);
 286		break;
 287	default:
 288		return -EINVAL;
 289	}
 290	if (err < 0)
 291		return err;
 292
 293	return len;
 294}
 295
 296static ssize_t counter_comp_u64_show(struct device *dev,
 297				     struct device_attribute *attr, char *buf)
 298{
 299	const struct counter_attribute *const a = to_counter_attribute(attr);
 300	struct counter_device *const counter = counter_from_dev(dev);
 301	int err;
 302	u64 data = 0;
 303
 304	switch (a->scope) {
 305	case COUNTER_SCOPE_DEVICE:
 306		err = a->comp.device_u64_read(counter, &data);
 307		break;
 308	case COUNTER_SCOPE_SIGNAL:
 309		err = a->comp.signal_u64_read(counter, a->parent, &data);
 310		break;
 311	case COUNTER_SCOPE_COUNT:
 312		err = a->comp.count_u64_read(counter, a->parent, &data);
 313		break;
 314	default:
 315		return -EINVAL;
 316	}
 317	if (err < 0)
 318		return err;
 319
 320	return sysfs_emit(buf, "%llu\n", (unsigned long long)data);
 321}
 322
 323static ssize_t counter_comp_u64_store(struct device *dev,
 324				      struct device_attribute *attr,
 325				      const char *buf, size_t len)
 326{
 327	const struct counter_attribute *const a = to_counter_attribute(attr);
 328	struct counter_device *const counter = counter_from_dev(dev);
 329	int err;
 330	u64 data = 0;
 331
 332	err = kstrtou64(buf, 0, &data);
 333	if (err < 0)
 334		return err;
 335
 336	switch (a->scope) {
 337	case COUNTER_SCOPE_DEVICE:
 338		err = a->comp.device_u64_write(counter, data);
 339		break;
 340	case COUNTER_SCOPE_SIGNAL:
 341		err = a->comp.signal_u64_write(counter, a->parent, data);
 342		break;
 343	case COUNTER_SCOPE_COUNT:
 344		err = a->comp.count_u64_write(counter, a->parent, data);
 345		break;
 346	default:
 347		return -EINVAL;
 348	}
 349	if (err < 0)
 350		return err;
 351
 352	return len;
 353}
 354
 355static ssize_t counter_comp_array_u32_show(struct device *dev,
 356					   struct device_attribute *attr,
 357					   char *buf)
 358{
 359	const struct counter_attribute *const a = to_counter_attribute(attr);
 360	struct counter_device *const counter = counter_from_dev(dev);
 361	const struct counter_array *const element = a->comp.priv;
 362	int err;
 363	u32 data = 0;
 364
 365	if (a->scope != COUNTER_SCOPE_SIGNAL ||
 366	    element->type != COUNTER_COMP_SIGNAL_POLARITY)
 367		return -EINVAL;
 368
 369	err = a->comp.signal_array_u32_read(counter, a->parent, element->idx,
 370					    &data);
 371	if (err < 0)
 372		return err;
 373
 374	return sysfs_emit(buf, "%s\n", counter_signal_polarity_str[data]);
 375}
 376
 377static ssize_t counter_comp_array_u32_store(struct device *dev,
 378					    struct device_attribute *attr,
 379					    const char *buf, size_t len)
 380{
 381	const struct counter_attribute *const a = to_counter_attribute(attr);
 382	struct counter_device *const counter = counter_from_dev(dev);
 383	const struct counter_array *const element = a->comp.priv;
 384	int err;
 385	u32 data = 0;
 386
 387	if (element->type != COUNTER_COMP_SIGNAL_POLARITY ||
 388	    a->scope != COUNTER_SCOPE_SIGNAL)
 389		return -EINVAL;
 390
 391	err = counter_find_enum(&data, element->avail->enums,
 392				element->avail->num_items, buf,
 393				counter_signal_polarity_str);
 394	if (err < 0)
 395		return err;
 396
 397	err = a->comp.signal_array_u32_write(counter, a->parent, element->idx,
 398					     data);
 399	if (err < 0)
 400		return err;
 401
 402	return len;
 403}
 404
 405static ssize_t counter_comp_array_u64_show(struct device *dev,
 406					   struct device_attribute *attr,
 407					   char *buf)
 408{
 409	const struct counter_attribute *const a = to_counter_attribute(attr);
 410	struct counter_device *const counter = counter_from_dev(dev);
 411	const struct counter_array *const element = a->comp.priv;
 412	int err;
 413	u64 data = 0;
 414
 415	switch (a->scope) {
 416	case COUNTER_SCOPE_DEVICE:
 417		err = a->comp.device_array_u64_read(counter, element->idx,
 418						    &data);
 419		break;
 420	case COUNTER_SCOPE_SIGNAL:
 421		err = a->comp.signal_array_u64_read(counter, a->parent,
 422						    element->idx, &data);
 423		break;
 424	case COUNTER_SCOPE_COUNT:
 425		err = a->comp.count_array_u64_read(counter, a->parent,
 426						   element->idx, &data);
 427		break;
 428	default:
 429		return -EINVAL;
 430	}
 431	if (err < 0)
 432		return err;
 433
 434	return sysfs_emit(buf, "%llu\n", (unsigned long long)data);
 435}
 436
 437static ssize_t counter_comp_array_u64_store(struct device *dev,
 438					    struct device_attribute *attr,
 439					    const char *buf, size_t len)
 440{
 441	const struct counter_attribute *const a = to_counter_attribute(attr);
 442	struct counter_device *const counter = counter_from_dev(dev);
 443	const struct counter_array *const element = a->comp.priv;
 444	int err;
 445	u64 data = 0;
 446
 447	err = kstrtou64(buf, 0, &data);
 448	if (err < 0)
 449		return err;
 450
 451	switch (a->scope) {
 452	case COUNTER_SCOPE_DEVICE:
 453		err = a->comp.device_array_u64_write(counter, element->idx,
 454						     data);
 455		break;
 456	case COUNTER_SCOPE_SIGNAL:
 457		err = a->comp.signal_array_u64_write(counter, a->parent,
 458						     element->idx, data);
 459		break;
 460	case COUNTER_SCOPE_COUNT:
 461		err = a->comp.count_array_u64_write(counter, a->parent,
 462						    element->idx, data);
 463		break;
 464	default:
 465		return -EINVAL;
 466	}
 467	if (err < 0)
 468		return err;
 469
 470	return len;
 471}
 472
 473static ssize_t enums_available_show(const u32 *const enums,
 474				    const size_t num_enums,
 475				    const char *const strs[], char *buf)
 476{
 477	size_t len = 0;
 478	size_t index;
 479
 480	for (index = 0; index < num_enums; index++)
 481		len += sysfs_emit_at(buf, len, "%s\n", strs[enums[index]]);
 482
 483	return len;
 484}
 485
 486static ssize_t strs_available_show(const struct counter_available *const avail,
 487				   char *buf)
 488{
 489	size_t len = 0;
 490	size_t index;
 491
 492	for (index = 0; index < avail->num_items; index++)
 493		len += sysfs_emit_at(buf, len, "%s\n", avail->strs[index]);
 494
 495	return len;
 496}
 497
 498static ssize_t counter_comp_available_show(struct device *dev,
 499					   struct device_attribute *attr,
 500					   char *buf)
 501{
 502	const struct counter_attribute *const a = to_counter_attribute(attr);
 503	const struct counter_count *const count = a->parent;
 504	const struct counter_synapse *const synapse = a->comp.priv;
 505	const struct counter_available *const avail = a->comp.priv;
 506
 507	switch (a->comp.type) {
 508	case COUNTER_COMP_FUNCTION:
 509		return enums_available_show(count->functions_list,
 510					    count->num_functions,
 511					    counter_function_str, buf);
 512	case COUNTER_COMP_SYNAPSE_ACTION:
 513		return enums_available_show(synapse->actions_list,
 514					    synapse->num_actions,
 515					    counter_synapse_action_str, buf);
 516	case COUNTER_COMP_ENUM:
 517		return strs_available_show(avail, buf);
 518	case COUNTER_COMP_COUNT_MODE:
 519		return enums_available_show(avail->enums, avail->num_items,
 520					    counter_count_mode_str, buf);
 521	default:
 522		return -EINVAL;
 523	}
 524}
 525
 526static int counter_avail_attr_create(struct device *const dev,
 527	struct counter_attribute_group *const group,
 528	const struct counter_comp *const comp, void *const parent)
 529{
 530	struct counter_attribute *counter_attr;
 531	struct device_attribute *dev_attr;
 532
 533	counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
 534	if (!counter_attr)
 535		return -ENOMEM;
 536
 537	/* Configure Counter attribute */
 538	counter_attr->comp.type = comp->type;
 539	counter_attr->comp.priv = comp->priv;
 540	counter_attr->parent = parent;
 541
 542	/* Initialize sysfs attribute */
 543	dev_attr = &counter_attr->dev_attr;
 544	sysfs_attr_init(&dev_attr->attr);
 545
 546	/* Configure device attribute */
 547	dev_attr->attr.name = devm_kasprintf(dev, GFP_KERNEL, "%s_available",
 548					     comp->name);
 549	if (!dev_attr->attr.name)
 550		return -ENOMEM;
 551	dev_attr->attr.mode = 0444;
 552	dev_attr->show = counter_comp_available_show;
 553
 554	/* Store list node */
 555	list_add(&counter_attr->l, &group->attr_list);
 556	group->num_attr++;
 557
 558	return 0;
 559}
 560
 561static int counter_attr_create(struct device *const dev,
 562			       struct counter_attribute_group *const group,
 563			       const struct counter_comp *const comp,
 564			       const enum counter_scope scope,
 565			       void *const parent)
 566{
 567	const struct counter_array *const array = comp->priv;
 568	struct counter_attribute *counter_attr;
 569	struct device_attribute *dev_attr;
 570
 571	counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
 572	if (!counter_attr)
 573		return -ENOMEM;
 574
 575	/* Configure Counter attribute */
 576	counter_attr->comp = *comp;
 577	counter_attr->scope = scope;
 578	counter_attr->parent = parent;
 579
 580	/* Configure device attribute */
 581	dev_attr = &counter_attr->dev_attr;
 582	sysfs_attr_init(&dev_attr->attr);
 583	dev_attr->attr.name = comp->name;
 584	switch (comp->type) {
 585	case COUNTER_COMP_U8:
 586	case COUNTER_COMP_BOOL:
 587		if (comp->device_u8_read) {
 588			dev_attr->attr.mode |= 0444;
 589			dev_attr->show = counter_comp_u8_show;
 590		}
 591		if (comp->device_u8_write) {
 592			dev_attr->attr.mode |= 0200;
 593			dev_attr->store = counter_comp_u8_store;
 594		}
 595		break;
 596	case COUNTER_COMP_SIGNAL_LEVEL:
 597	case COUNTER_COMP_FUNCTION:
 598	case COUNTER_COMP_SYNAPSE_ACTION:
 599	case COUNTER_COMP_ENUM:
 600	case COUNTER_COMP_COUNT_DIRECTION:
 601	case COUNTER_COMP_COUNT_MODE:
 602	case COUNTER_COMP_SIGNAL_POLARITY:
 603		if (comp->device_u32_read) {
 604			dev_attr->attr.mode |= 0444;
 605			dev_attr->show = counter_comp_u32_show;
 606		}
 607		if (comp->device_u32_write) {
 608			dev_attr->attr.mode |= 0200;
 609			dev_attr->store = counter_comp_u32_store;
 610		}
 611		break;
 612	case COUNTER_COMP_U64:
 613		if (comp->device_u64_read) {
 614			dev_attr->attr.mode |= 0444;
 615			dev_attr->show = counter_comp_u64_show;
 616		}
 617		if (comp->device_u64_write) {
 618			dev_attr->attr.mode |= 0200;
 619			dev_attr->store = counter_comp_u64_store;
 620		}
 621		break;
 622	case COUNTER_COMP_ARRAY:
 623		switch (array->type) {
 624		case COUNTER_COMP_SIGNAL_POLARITY:
 625			if (comp->signal_array_u32_read) {
 626				dev_attr->attr.mode |= 0444;
 627				dev_attr->show = counter_comp_array_u32_show;
 628			}
 629			if (comp->signal_array_u32_write) {
 630				dev_attr->attr.mode |= 0200;
 631				dev_attr->store = counter_comp_array_u32_store;
 632			}
 633			break;
 634		case COUNTER_COMP_U64:
 635			if (comp->device_array_u64_read) {
 636				dev_attr->attr.mode |= 0444;
 637				dev_attr->show = counter_comp_array_u64_show;
 638			}
 639			if (comp->device_array_u64_write) {
 640				dev_attr->attr.mode |= 0200;
 641				dev_attr->store = counter_comp_array_u64_store;
 642			}
 643			break;
 644		default:
 645			return -EINVAL;
 646		}
 647		break;
 648	default:
 649		return -EINVAL;
 650	}
 651
 652	/* Store list node */
 653	list_add(&counter_attr->l, &group->attr_list);
 654	group->num_attr++;
 655
 656	/* Create "*_available" attribute if needed */
 657	switch (comp->type) {
 658	case COUNTER_COMP_FUNCTION:
 659	case COUNTER_COMP_SYNAPSE_ACTION:
 660	case COUNTER_COMP_ENUM:
 661	case COUNTER_COMP_COUNT_MODE:
 662		return counter_avail_attr_create(dev, group, comp, parent);
 663	default:
 664		return 0;
 665	}
 666}
 667
 668static ssize_t counter_comp_name_show(struct device *dev,
 669				      struct device_attribute *attr, char *buf)
 670{
 671	return sysfs_emit(buf, "%s\n", to_counter_attribute(attr)->comp.name);
 672}
 673
 674static int counter_name_attr_create(struct device *const dev,
 675				    struct counter_attribute_group *const group,
 676				    const char *const name)
 677{
 678	struct counter_attribute *counter_attr;
 679
 680	counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
 681	if (!counter_attr)
 682		return -ENOMEM;
 683
 684	/* Configure Counter attribute */
 685	counter_attr->comp.name = name;
 686
 687	/* Configure device attribute */
 688	sysfs_attr_init(&counter_attr->dev_attr.attr);
 689	counter_attr->dev_attr.attr.name = "name";
 690	counter_attr->dev_attr.attr.mode = 0444;
 691	counter_attr->dev_attr.show = counter_comp_name_show;
 692
 693	/* Store list node */
 694	list_add(&counter_attr->l, &group->attr_list);
 695	group->num_attr++;
 696
 697	return 0;
 698}
 699
 700static ssize_t counter_comp_id_show(struct device *dev,
 701				    struct device_attribute *attr, char *buf)
 702{
 703	const size_t id = (size_t)to_counter_attribute(attr)->comp.priv;
 704
 705	return sysfs_emit(buf, "%zu\n", id);
 706}
 707
 708static int counter_comp_id_attr_create(struct device *const dev,
 709				       struct counter_attribute_group *const group,
 710				       const char *name, const size_t id)
 711{
 712	struct counter_attribute *counter_attr;
 713
 714	/* Allocate Counter attribute */
 715	counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
 716	if (!counter_attr)
 717		return -ENOMEM;
 718
 719	/* Generate component ID name */
 720	name = devm_kasprintf(dev, GFP_KERNEL, "%s_component_id", name);
 721	if (!name)
 722		return -ENOMEM;
 723
 724	/* Configure Counter attribute */
 725	counter_attr->comp.priv = (void *)id;
 726
 727	/* Configure device attribute */
 728	sysfs_attr_init(&counter_attr->dev_attr.attr);
 729	counter_attr->dev_attr.attr.name = name;
 730	counter_attr->dev_attr.attr.mode = 0444;
 731	counter_attr->dev_attr.show = counter_comp_id_show;
 732
 733	/* Store list node */
 734	list_add(&counter_attr->l, &group->attr_list);
 735	group->num_attr++;
 736
 737	return 0;
 738}
 739
 740static int counter_ext_attrs_create(struct device *const dev,
 741				    struct counter_attribute_group *const group,
 742				    const struct counter_comp *const ext,
 743				    const enum counter_scope scope,
 744				    void *const parent, const size_t id)
 745{
 746	int err;
 747
 748	/* Create main extension attribute */
 749	err = counter_attr_create(dev, group, ext, scope, parent);
 750	if (err < 0)
 751		return err;
 752
 753	/* Create extension id attribute */
 754	return counter_comp_id_attr_create(dev, group, ext->name, id);
 755}
 756
 757static int counter_array_attrs_create(struct device *const dev,
 758				      struct counter_attribute_group *const group,
 759				      const struct counter_comp *const comp,
 760				      const enum counter_scope scope,
 761				      void *const parent, const size_t id)
 762{
 763	const struct counter_array *const array = comp->priv;
 764	struct counter_comp ext = *comp;
 765	struct counter_array *element;
 766	size_t idx;
 767	int err;
 768
 769	/* Create an attribute for each array element */
 770	for (idx = 0; idx < array->length; idx++) {
 771		/* Generate array element attribute name */
 772		ext.name = devm_kasprintf(dev, GFP_KERNEL, "%s%zu", comp->name,
 773					  idx);
 774		if (!ext.name)
 775			return -ENOMEM;
 776
 777		/* Allocate and configure array element */
 778		element = devm_kzalloc(dev, sizeof(*element), GFP_KERNEL);
 779		if (!element)
 780			return -ENOMEM;
 781		element->type = array->type;
 782		element->avail = array->avail;
 783		element->idx = idx;
 784		ext.priv = element;
 785
 786		/* Create all attributes associated with the array element */
 787		err = counter_ext_attrs_create(dev, group, &ext, scope, parent,
 788					       id + idx);
 789		if (err < 0)
 790			return err;
 791	}
 792
 793	return 0;
 794}
 795
 796static int counter_sysfs_exts_add(struct device *const dev,
 797				  struct counter_attribute_group *const group,
 798				  const struct counter_comp *const exts,
 799				  const size_t num_ext,
 800				  const enum counter_scope scope,
 801				  void *const parent)
 802{
 803	size_t i;
 804	const struct counter_comp *ext;
 805	int err;
 806	size_t id = 0;
 807	const struct counter_array *array;
 808
 809	/* Create attributes for each extension */
 810	for (i = 0; i < num_ext; i++) {
 811		ext = &exts[i];
 812		if (ext->type == COUNTER_COMP_ARRAY) {
 813			err = counter_array_attrs_create(dev, group, ext, scope,
 814							 parent, id);
 815			array = ext->priv;
 816			id += array->length;
 817		} else {
 818			err = counter_ext_attrs_create(dev, group, ext, scope,
 819						       parent, id);
 820			id++;
 821		}
 822		if (err < 0)
 823			return err;
 824	}
 825
 826	return 0;
 827}
 828
 829static struct counter_comp counter_signal_comp = {
 830	.type = COUNTER_COMP_SIGNAL_LEVEL,
 831	.name = "signal",
 832};
 833
 834static int counter_signal_attrs_create(struct counter_device *const counter,
 835	struct counter_attribute_group *const cattr_group,
 836	struct counter_signal *const signal)
 837{
 838	const enum counter_scope scope = COUNTER_SCOPE_SIGNAL;
 839	struct device *const dev = &counter->dev;
 840	int err;
 841	struct counter_comp comp;
 842
 843	/* Create main Signal attribute */
 844	comp = counter_signal_comp;
 845	comp.signal_u32_read = counter->ops->signal_read;
 846	err = counter_attr_create(dev, cattr_group, &comp, scope, signal);
 847	if (err < 0)
 848		return err;
 849
 850	/* Create Signal name attribute */
 851	err = counter_name_attr_create(dev, cattr_group, signal->name);
 852	if (err < 0)
 853		return err;
 854
 855	/* Add Signal extensions */
 856	return counter_sysfs_exts_add(dev, cattr_group, signal->ext,
 857				      signal->num_ext, scope, signal);
 858}
 859
 860static int counter_sysfs_signals_add(struct counter_device *const counter,
 861	struct counter_attribute_group *const groups)
 862{
 863	size_t i;
 864	int err;
 865
 866	/* Add each Signal */
 867	for (i = 0; i < counter->num_signals; i++) {
 868		/* Generate Signal attribute directory name */
 869		groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL,
 870						"signal%zu", i);
 871		if (!groups[i].name)
 872			return -ENOMEM;
 873
 874		/* Create all attributes associated with Signal */
 875		err = counter_signal_attrs_create(counter, groups + i,
 876						  counter->signals + i);
 877		if (err < 0)
 878			return err;
 879	}
 880
 881	return 0;
 882}
 883
 884static int counter_sysfs_synapses_add(struct counter_device *const counter,
 885	struct counter_attribute_group *const group,
 886	struct counter_count *const count)
 887{
 888	size_t i;
 889
 890	/* Add each Synapse */
 891	for (i = 0; i < count->num_synapses; i++) {
 892		struct device *const dev = &counter->dev;
 893		struct counter_synapse *synapse;
 894		size_t id;
 895		struct counter_comp comp;
 896		int err;
 897
 898		synapse = count->synapses + i;
 899
 900		/* Generate Synapse action name */
 901		id = synapse->signal - counter->signals;
 902		comp.name = devm_kasprintf(dev, GFP_KERNEL, "signal%zu_action",
 903					   id);
 904		if (!comp.name)
 905			return -ENOMEM;
 906
 907		/* Create action attribute */
 908		comp.type = COUNTER_COMP_SYNAPSE_ACTION;
 909		comp.action_read = counter->ops->action_read;
 910		comp.action_write = counter->ops->action_write;
 911		comp.priv = synapse;
 912		err = counter_attr_create(dev, group, &comp,
 913					  COUNTER_SCOPE_COUNT, count);
 914		if (err < 0)
 915			return err;
 916
 917		/* Create Synapse component ID attribute */
 918		err = counter_comp_id_attr_create(dev, group, comp.name, i);
 919		if (err < 0)
 920			return err;
 921	}
 922
 923	return 0;
 924}
 925
 926static struct counter_comp counter_count_comp =
 927	COUNTER_COMP_COUNT_U64("count", NULL, NULL);
 928
 929static struct counter_comp counter_function_comp = {
 930	.type = COUNTER_COMP_FUNCTION,
 931	.name = "function",
 932};
 933
 934static int counter_count_attrs_create(struct counter_device *const counter,
 935	struct counter_attribute_group *const cattr_group,
 936	struct counter_count *const count)
 937{
 938	const enum counter_scope scope = COUNTER_SCOPE_COUNT;
 939	struct device *const dev = &counter->dev;
 940	int err;
 941	struct counter_comp comp;
 942
 943	/* Create main Count attribute */
 944	comp = counter_count_comp;
 945	comp.count_u64_read = counter->ops->count_read;
 946	comp.count_u64_write = counter->ops->count_write;
 947	err = counter_attr_create(dev, cattr_group, &comp, scope, count);
 948	if (err < 0)
 949		return err;
 950
 951	/* Create Count name attribute */
 952	err = counter_name_attr_create(dev, cattr_group, count->name);
 953	if (err < 0)
 954		return err;
 955
 956	/* Create Count function attribute */
 957	comp = counter_function_comp;
 958	comp.count_u32_read = counter->ops->function_read;
 959	comp.count_u32_write = counter->ops->function_write;
 960	err = counter_attr_create(dev, cattr_group, &comp, scope, count);
 961	if (err < 0)
 962		return err;
 963
 964	/* Add Count extensions */
 965	return counter_sysfs_exts_add(dev, cattr_group, count->ext,
 966				      count->num_ext, scope, count);
 967}
 968
 969static int counter_sysfs_counts_add(struct counter_device *const counter,
 970	struct counter_attribute_group *const groups)
 971{
 972	size_t i;
 973	struct counter_count *count;
 974	int err;
 975
 976	/* Add each Count */
 977	for (i = 0; i < counter->num_counts; i++) {
 978		count = counter->counts + i;
 979
 980		/* Generate Count attribute directory name */
 981		groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL,
 982						"count%zu", i);
 983		if (!groups[i].name)
 984			return -ENOMEM;
 985
 986		/* Add sysfs attributes of the Synapses */
 987		err = counter_sysfs_synapses_add(counter, groups + i, count);
 988		if (err < 0)
 989			return err;
 990
 991		/* Create all attributes associated with Count */
 992		err = counter_count_attrs_create(counter, groups + i, count);
 993		if (err < 0)
 994			return err;
 995	}
 996
 997	return 0;
 998}
 999
1000static int counter_num_signals_read(struct counter_device *counter, u8 *val)
1001{
1002	*val = counter->num_signals;
1003	return 0;
1004}
1005
1006static int counter_num_counts_read(struct counter_device *counter, u8 *val)
1007{
1008	*val = counter->num_counts;
1009	return 0;
1010}
1011
1012static int counter_events_queue_size_read(struct counter_device *counter,
1013					  u64 *val)
1014{
1015	*val = kfifo_size(&counter->events);
1016	return 0;
1017}
1018
1019static int counter_events_queue_size_write(struct counter_device *counter,
1020					   u64 val)
1021{
1022	DECLARE_KFIFO_PTR(events, struct counter_event);
1023	int err;
1024	unsigned long flags;
1025
1026	/* Allocate new events queue */
1027	err = kfifo_alloc(&events, val, GFP_KERNEL);
1028	if (err)
1029		return err;
1030
1031	/* Swap in new events queue */
1032	mutex_lock(&counter->events_out_lock);
1033	spin_lock_irqsave(&counter->events_in_lock, flags);
1034	kfifo_free(&counter->events);
1035	counter->events.kfifo = events.kfifo;
1036	spin_unlock_irqrestore(&counter->events_in_lock, flags);
1037	mutex_unlock(&counter->events_out_lock);
1038
1039	return 0;
1040}
1041
1042static struct counter_comp counter_num_signals_comp =
1043	COUNTER_COMP_DEVICE_U8("num_signals", counter_num_signals_read, NULL);
1044
1045static struct counter_comp counter_num_counts_comp =
1046	COUNTER_COMP_DEVICE_U8("num_counts", counter_num_counts_read, NULL);
1047
1048static struct counter_comp counter_events_queue_size_comp =
1049	COUNTER_COMP_DEVICE_U64("events_queue_size",
1050				counter_events_queue_size_read,
1051				counter_events_queue_size_write);
1052
1053static int counter_sysfs_attr_add(struct counter_device *const counter,
1054				  struct counter_attribute_group *cattr_group)
1055{
1056	const enum counter_scope scope = COUNTER_SCOPE_DEVICE;
1057	struct device *const dev = &counter->dev;
1058	int err;
1059
1060	/* Add Signals sysfs attributes */
1061	err = counter_sysfs_signals_add(counter, cattr_group);
1062	if (err < 0)
1063		return err;
1064	cattr_group += counter->num_signals;
1065
1066	/* Add Counts sysfs attributes */
1067	err = counter_sysfs_counts_add(counter, cattr_group);
1068	if (err < 0)
1069		return err;
1070	cattr_group += counter->num_counts;
1071
1072	/* Create name attribute */
1073	err = counter_name_attr_create(dev, cattr_group, counter->name);
1074	if (err < 0)
1075		return err;
1076
1077	/* Create num_signals attribute */
1078	err = counter_attr_create(dev, cattr_group, &counter_num_signals_comp,
1079				  scope, NULL);
1080	if (err < 0)
1081		return err;
1082
1083	/* Create num_counts attribute */
1084	err = counter_attr_create(dev, cattr_group, &counter_num_counts_comp,
1085				  scope, NULL);
1086	if (err < 0)
1087		return err;
1088
1089	/* Create events_queue_size attribute */
1090	err = counter_attr_create(dev, cattr_group,
1091				  &counter_events_queue_size_comp, scope, NULL);
1092	if (err < 0)
1093		return err;
1094
1095	/* Add device extensions */
1096	return counter_sysfs_exts_add(dev, cattr_group, counter->ext,
1097				      counter->num_ext, scope, NULL);
1098
1099	return 0;
1100}
1101
1102/**
1103 * counter_sysfs_add - Adds Counter sysfs attributes to the device structure
1104 * @counter:	Pointer to the Counter device structure
1105 *
1106 * Counter sysfs attributes are created and added to the respective device
1107 * structure for later registration to the system. Resource-managed memory
1108 * allocation is performed by this function, and this memory should be freed
1109 * when no longer needed (automatically by a device_unregister call, or
1110 * manually by a devres_release_all call).
1111 */
1112int counter_sysfs_add(struct counter_device *const counter)
1113{
1114	struct device *const dev = &counter->dev;
1115	const size_t num_groups = counter->num_signals + counter->num_counts + 1;
1116	struct counter_attribute_group *cattr_groups;
1117	size_t i, j;
1118	int err;
1119	struct attribute_group *groups;
1120	struct counter_attribute *p;
1121
1122	/* Allocate space for attribute groups (signals, counts, and ext) */
1123	cattr_groups = devm_kcalloc(dev, num_groups, sizeof(*cattr_groups),
1124				    GFP_KERNEL);
1125	if (!cattr_groups)
1126		return -ENOMEM;
1127
1128	/* Initialize attribute lists */
1129	for (i = 0; i < num_groups; i++)
1130		INIT_LIST_HEAD(&cattr_groups[i].attr_list);
1131
1132	/* Add Counter device sysfs attributes */
1133	err = counter_sysfs_attr_add(counter, cattr_groups);
1134	if (err < 0)
1135		return err;
1136
1137	/* Allocate attribute group pointers for association with device */
1138	dev->groups = devm_kcalloc(dev, num_groups + 1, sizeof(*dev->groups),
1139				   GFP_KERNEL);
1140	if (!dev->groups)
1141		return -ENOMEM;
1142
1143	/* Allocate space for attribute groups */
1144	groups = devm_kcalloc(dev, num_groups, sizeof(*groups), GFP_KERNEL);
1145	if (!groups)
1146		return -ENOMEM;
1147
1148	/* Prepare each group of attributes for association */
1149	for (i = 0; i < num_groups; i++) {
1150		groups[i].name = cattr_groups[i].name;
1151
1152		/* Allocate space for attribute pointers */
1153		groups[i].attrs = devm_kcalloc(dev,
1154					       cattr_groups[i].num_attr + 1,
1155					       sizeof(*groups[i].attrs),
1156					       GFP_KERNEL);
1157		if (!groups[i].attrs)
1158			return -ENOMEM;
1159
1160		/* Add attribute pointers to attribute group */
1161		j = 0;
1162		list_for_each_entry(p, &cattr_groups[i].attr_list, l)
1163			groups[i].attrs[j++] = &p->dev_attr.attr;
1164
1165		/* Associate attribute group */
1166		dev->groups[i] = &groups[i];
1167	}
1168
1169	return 0;
1170}