162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/spinlock.h>
762306a36Sopenharmony_ci#include <linux/list.h>
862306a36Sopenharmony_ci#include <linux/sched/signal.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/export.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "w1_internal.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ciDEFINE_SPINLOCK(w1_flock);
1562306a36Sopenharmony_cistatic LIST_HEAD(w1_families);
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/**
1862306a36Sopenharmony_ci * w1_register_family() - register a device family driver
1962306a36Sopenharmony_ci * @newf:	family to register
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ciint w1_register_family(struct w1_family *newf)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	struct list_head *ent, *n;
2462306a36Sopenharmony_ci	struct w1_family *f;
2562306a36Sopenharmony_ci	int ret = 0;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	spin_lock(&w1_flock);
2862306a36Sopenharmony_ci	list_for_each_safe(ent, n, &w1_families) {
2962306a36Sopenharmony_ci		f = list_entry(ent, struct w1_family, family_entry);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci		if (f->fid == newf->fid) {
3262306a36Sopenharmony_ci			ret = -EEXIST;
3362306a36Sopenharmony_ci			break;
3462306a36Sopenharmony_ci		}
3562306a36Sopenharmony_ci	}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	if (!ret) {
3862306a36Sopenharmony_ci		atomic_set(&newf->refcnt, 0);
3962306a36Sopenharmony_ci		list_add_tail(&newf->family_entry, &w1_families);
4062306a36Sopenharmony_ci	}
4162306a36Sopenharmony_ci	spin_unlock(&w1_flock);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/* check default devices against the new set of drivers */
4462306a36Sopenharmony_ci	w1_reconnect_slaves(newf, 1);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	return ret;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ciEXPORT_SYMBOL(w1_register_family);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/**
5162306a36Sopenharmony_ci * w1_unregister_family() - unregister a device family driver
5262306a36Sopenharmony_ci * @fent:	family to unregister
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_civoid w1_unregister_family(struct w1_family *fent)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct list_head *ent, *n;
5762306a36Sopenharmony_ci	struct w1_family *f;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	spin_lock(&w1_flock);
6062306a36Sopenharmony_ci	list_for_each_safe(ent, n, &w1_families) {
6162306a36Sopenharmony_ci		f = list_entry(ent, struct w1_family, family_entry);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci		if (f->fid == fent->fid) {
6462306a36Sopenharmony_ci			list_del(&fent->family_entry);
6562306a36Sopenharmony_ci			break;
6662306a36Sopenharmony_ci		}
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci	spin_unlock(&w1_flock);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/* deatch devices using this family code */
7162306a36Sopenharmony_ci	w1_reconnect_slaves(fent, 0);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	while (atomic_read(&fent->refcnt)) {
7462306a36Sopenharmony_ci		pr_info("Waiting for family %u to become free: refcnt=%d.\n",
7562306a36Sopenharmony_ci				fent->fid, atomic_read(&fent->refcnt));
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci		if (msleep_interruptible(1000))
7862306a36Sopenharmony_ci			flush_signals(current);
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ciEXPORT_SYMBOL(w1_unregister_family);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/*
8462306a36Sopenharmony_ci * Should be called under w1_flock held.
8562306a36Sopenharmony_ci */
8662306a36Sopenharmony_cistruct w1_family * w1_family_registered(u8 fid)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct list_head *ent, *n;
8962306a36Sopenharmony_ci	struct w1_family *f = NULL;
9062306a36Sopenharmony_ci	int ret = 0;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	list_for_each_safe(ent, n, &w1_families) {
9362306a36Sopenharmony_ci		f = list_entry(ent, struct w1_family, family_entry);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		if (f->fid == fid) {
9662306a36Sopenharmony_ci			ret = 1;
9762306a36Sopenharmony_ci			break;
9862306a36Sopenharmony_ci		}
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return (ret) ? f : NULL;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic void __w1_family_put(struct w1_family *f)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	atomic_dec(&f->refcnt);
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_civoid w1_family_put(struct w1_family *f)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	spin_lock(&w1_flock);
11262306a36Sopenharmony_ci	__w1_family_put(f);
11362306a36Sopenharmony_ci	spin_unlock(&w1_flock);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci#if 0
11762306a36Sopenharmony_civoid w1_family_get(struct w1_family *f)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	spin_lock(&w1_flock);
12062306a36Sopenharmony_ci	__w1_family_get(f);
12162306a36Sopenharmony_ci	spin_unlock(&w1_flock);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci#endif  /*  0  */
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_civoid __w1_family_get(struct w1_family *f)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	smp_mb__before_atomic();
12862306a36Sopenharmony_ci	atomic_inc(&f->refcnt);
12962306a36Sopenharmony_ci	smp_mb__after_atomic();
13062306a36Sopenharmony_ci}
131