summaryrefslogtreecommitdiffstats
path: root/drivers/staging/greybus/module.c
blob: 699cd003e3673d4f5aec907d9847be06fc3e7965 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/*
 * Greybus modules
 *
 * Copyright 2014 Google Inc.
 *
 * Released under the GPLv2 only.
 */

#include "greybus.h"

/* XXX This could be per-host device */
static DEFINE_SPINLOCK(gb_modules_lock);

static int gb_module_match_one_id(struct gb_module *gmod,
				const struct greybus_module_id *id)
{
	if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_VENDOR) &&
	    (id->vendor != gmod->vendor))
		return 0;

	if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_PRODUCT) &&
	    (id->product != gmod->product))
		return 0;

	if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_SERIAL) &&
	    (id->unique_id != gmod->unique_id))
		return 0;

	return 1;
}

const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod,
				const struct greybus_module_id *id)
{
	if (id == NULL)
		return NULL;

	for (; id->vendor || id->product || id->unique_id ||
			id->driver_info; id++) {
		if (gb_module_match_one_id(gmod, id))
			return id;
	}

	return NULL;
}

/*
 * A Greybus module represents a user-replacable component on an Ara
 * phone.
 *
 * Create a gb_module structure to represent a discovered module.
 * The position within the Endo is encoded in the "module_id" argument.
 * Returns a pointer to the new module or a null pointer if a
 * failure occurs due to memory exhaustion.
 */
struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id)
{
	struct gb_module *gmod;

	gmod = kzalloc(sizeof(*gmod), GFP_KERNEL);
	if (!gmod)
		return NULL;

	gmod->hd = hd;		/* XXX refcount? */
	gmod->module_id = module_id;
	INIT_LIST_HEAD(&gmod->interfaces);

	spin_lock_irq(&gb_modules_lock);
	list_add_tail(&gmod->links, &hd->modules);
	spin_unlock_irq(&gb_modules_lock);

	return gmod;
}

/*
 * Tear down a previously set up module.
 */
void gb_module_destroy(struct gb_module *gmod)
{
	if (WARN_ON(!gmod))
		return;

	kfree(gmod->product_string);
	kfree(gmod->vendor_string);

	spin_lock_irq(&gb_modules_lock);
	list_del(&gmod->links);
	spin_unlock_irq(&gb_modules_lock);

	/* kref_put(module->hd); */

	kfree(gmod);
}

void gb_module_interfaces_init(struct gb_module *gmod)
{
	struct gb_interface *interface;
	int ret = 0;

	list_for_each_entry(interface, &gmod->interfaces, links) {
		ret = gb_interface_connections_init(interface);
		if (ret)
			dev_err(gmod->hd->parent,
				"module interface init error %d\n", ret);
	}
}