summaryrefslogtreecommitdiffstats
path: root/drivers/staging/greybus/interface.c
blob: a7375a2809d534cdbeeaec951440150a1e721999 (plain) (blame)
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
 * Greybus interfaces
 *
 * Copyright 2014 Google Inc.
 *
 * Released under the GPLv2 only.
 */

#include "greybus.h"

static ssize_t device_id_show(struct device *dev, struct device_attribute *attr,
			      char *buf)
{
	struct gb_interface *interface = to_gb_interface(dev);

	return sprintf(buf, "%d", interface->device_id);
}
static DEVICE_ATTR_RO(device_id);

static struct attribute *interface_attrs[] = {
	&dev_attr_device_id.attr,
	NULL,
};

ATTRIBUTE_GROUPS(interface);

static void gb_interface_release(struct device *dev)
{
	struct gb_interface *interface = to_gb_interface(dev);

	kfree(interface);
}

static struct device_type greybus_interface_type = {
	.name =		"greybus_interface",
	.release =	gb_interface_release,
};


/* XXX This could be per-host device or per-module */
static DEFINE_SPINLOCK(gb_interfaces_lock);

/*
 * A Greybus interface represents a UniPro device present on a
 * module.  For Project Ara, each active Interface Block on a module
 * implements a UniPro device, and therefore a Greybus interface.  A
 * Greybus module has at least one interface, but can have two (or
 * even more).
 *
 * Create a gb_interface structure to represent a discovered
 * interface.  Returns a pointer to the new interface or a null
 * pointer if a failure occurs due to memory exhaustion.
 */
struct gb_interface *
gb_interface_create(struct gb_module *gmod, u8 interface_id)
{
	struct gb_interface *interface;
	int retval;

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

	interface->gmod = gmod;		/* XXX refcount? */
	interface->id = interface_id;
	interface->device_id = 0xff;	/* Invalid device id to start with */
	INIT_LIST_HEAD(&interface->connections);

	/* Build up the interface device structures and register it with the
	 * driver core */
	interface->dev.parent = &gmod->dev;
	interface->dev.driver = NULL;
	interface->dev.bus = &greybus_bus_type;
	interface->dev.type = &greybus_interface_type;
	interface->dev.groups = interface_groups;
	device_initialize(&interface->dev);
	dev_set_name(&interface->dev, "%d:%d", gmod->module_id, interface_id);

	retval = device_add(&interface->dev);
	if (retval) {
		kfree(interface);
		return NULL;
	}

	spin_lock_irq(&gb_interfaces_lock);
	list_add_tail(&interface->links, &gmod->interfaces);
	spin_unlock_irq(&gb_interfaces_lock);

	return interface;
}

/*
 * Tear down a previously set up interface.
 */
void gb_interface_destroy(struct gb_module *gmod)
{
	struct gb_interface *interface;
	struct gb_interface *temp;

	if (WARN_ON(!gmod))
		return;

	spin_lock_irq(&gb_interfaces_lock);
	list_for_each_entry_safe(interface, temp, &gmod->interfaces, links) {
		list_del(&interface->links);
		gb_interface_connections_exit(interface);
		device_del(&interface->dev);
	}
	spin_unlock_irq(&gb_interfaces_lock);
}

struct gb_interface *gb_interface_find(struct gb_module *module,
				      u8 interface_id)
{
	struct gb_interface *interface;

	spin_lock_irq(&gb_interfaces_lock);
	list_for_each_entry(interface, &module->interfaces, links)
		if (interface->id == interface_id) {
			spin_unlock_irq(&gb_interfaces_lock);
			return interface;
		}
	spin_unlock_irq(&gb_interfaces_lock);

	return NULL;
}

int gb_interface_connections_init(struct gb_interface *interface)
{
	struct gb_connection *connection;
	int ret = 0;

	list_for_each_entry(connection, &interface->connections,
			interface_links) {
		ret = gb_connection_init(connection);
		if (ret)
			break;
	}

	return ret;
}

void gb_interface_connections_exit(struct gb_interface *interface)
{
	struct gb_connection *connection;
	struct gb_connection *next;

	list_for_each_entry_safe(connection, next, &interface->connections,
			interface_links) {
		gb_connection_exit(connection);
		gb_connection_destroy(connection);
	}
}