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