]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/greybus/protocol.c
greybus: gpb: Create a "GP Bridge" kernel module
[karo-tx-linux.git] / drivers / staging / greybus / protocol.c
1 /*
2  * Greybus protocol handling
3  *
4  * Copyright 2014 Google Inc.
5  * Copyright 2014 Linaro Ltd.
6  *
7  * Released under the GPLv2 only.
8  */
9
10 #include "greybus.h"
11
12 /* Global list of registered protocols */
13 static DEFINE_SPINLOCK(gb_protocols_lock);
14 static LIST_HEAD(gb_protocols);
15
16 /* Caller must hold gb_protocols_lock */
17 static struct gb_protocol *_gb_protocol_find(u8 id, u8 major, u8 minor)
18 {
19         struct gb_protocol *protocol;
20
21         list_for_each_entry(protocol, &gb_protocols, links) {
22                 if (protocol->id < id)
23                         continue;
24                 if (protocol->id > id)
25                         break;
26
27                 if (protocol->major > major)
28                         continue;
29                 if (protocol->major < major)
30                         break;
31
32                 if (protocol->minor > minor)
33                         continue;
34                 if (protocol->minor < minor)
35                         break;
36
37                 return protocol;
38         }
39         return NULL;
40 }
41
42 int __gb_protocol_register(struct gb_protocol *protocol, struct module *module)
43 {
44         struct gb_protocol *existing;
45         u8 id = protocol->id;
46         u8 major = protocol->major;
47         u8 minor = protocol->minor;
48
49         protocol->owner = module;
50
51         /*
52          * The protocols list is sorted first by protocol id (low to
53          * high), then by major version (high to low), and finally
54          * by minor version (high to low).  Searching only by
55          * protocol id will produce the newest implemented version
56          * of the protocol.
57          */
58         spin_lock_irq(&gb_protocols_lock);
59
60         list_for_each_entry(existing, &gb_protocols, links) {
61                 if (existing->id < id)
62                         continue;
63                 if (existing->id > id)
64                         break;
65
66                 if (existing->major > major)
67                         continue;
68                 if (existing->major < major)
69                         break;
70
71                 if (existing->minor > minor)
72                         continue;
73                 if (existing->minor < minor)
74                         break;
75
76                 /* A matching protocol has already been registered */
77                 spin_unlock_irq(&gb_protocols_lock);
78
79                 return -EEXIST;
80         }
81
82         /*
83          * We need to insert the protocol here, before the existing one
84          * (or before the head if we searched the whole list)
85          */
86         list_add_tail(&protocol->links, &existing->links);
87         spin_unlock_irq(&gb_protocols_lock);
88
89         /*
90          * Go try to bind any unbound connections, as we have a
91          * new protocol in the system
92          */
93         gb_bundle_bind_protocols();
94
95         return 0;
96 }
97 EXPORT_SYMBOL_GPL(__gb_protocol_register);
98
99 /*
100  * De-register a previously registered protocol.
101  *
102  * XXX Currently this fails (and reports an error to the caller) if
103  * XXX the protocol is currently in use.  We may want to forcefully
104  * XXX kill off a protocol and all its active users at some point.
105  * XXX But I think that's better handled by quiescing modules that
106  * XXX have users and having those users drop their reference.
107  *
108  * Returns true if successful, false otherwise.
109  */
110 int gb_protocol_deregister(struct gb_protocol *protocol)
111 {
112         u8 protocol_count = 0;
113
114         if (!protocol)
115                 return 0;
116
117         spin_lock_irq(&gb_protocols_lock);
118         protocol = _gb_protocol_find(protocol->id, protocol->major,
119                                                 protocol->minor);
120         if (protocol) {
121                 protocol_count = protocol->count;
122                 if (!protocol_count)
123                         list_del(&protocol->links);
124         }
125         spin_unlock_irq(&gb_protocols_lock);
126
127         return protocol && !protocol_count;
128 }
129 EXPORT_SYMBOL_GPL(gb_protocol_deregister);
130
131 /* Returns the requested protocol if available, or a null pointer */
132 struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor)
133 {
134         struct gb_protocol *protocol;
135         u8 protocol_count;
136
137         spin_lock_irq(&gb_protocols_lock);
138         protocol = _gb_protocol_find(id, major, minor);
139         if (protocol) {
140                 if (!try_module_get(protocol->owner)) {
141                         protocol = NULL;
142                 } else {
143                         protocol_count = protocol->count;
144                         if (protocol_count != U8_MAX)
145                                 protocol->count++;
146                 }
147         }
148         spin_unlock_irq(&gb_protocols_lock);
149
150         if (protocol)
151                 WARN_ON(protocol_count == U8_MAX);
152         else
153                 pr_err("protocol id %hhu version %hhu.%hhu not found\n",
154                         id, major, minor);
155
156         return protocol;
157 }
158
159 void gb_protocol_put(struct gb_protocol *protocol)
160 {
161         u8 major = protocol->major;
162         u8 minor = protocol->minor;
163         u8 protocol_count;
164
165         spin_lock_irq(&gb_protocols_lock);
166         protocol = _gb_protocol_find(protocol->id, protocol->major,
167                                                 protocol->minor);
168         if (protocol) {
169                 protocol_count = protocol->count;
170                 if (protocol_count)
171                         protocol->count--;
172                 module_put(protocol->owner);
173         }
174         spin_unlock_irq(&gb_protocols_lock);
175         if (protocol)
176                 WARN_ON(!protocol_count);
177         else
178                 pr_err("protocol id %hhu version %hhu.%hhu not found\n",
179                         protocol->id, major, minor);
180 }