]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/greybus/protocol.c
889cff2a10c2354710cc62f18dfe17467bf030d8
[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 void gb_protocol_deregister(struct gb_protocol *protocol)
107 {
108         if (!protocol)
109                 return;
110
111         spin_lock_irq(&gb_protocols_lock);
112         protocol = gb_protocol_find(protocol->id, protocol->major,
113                                     protocol->minor);
114         if (WARN_ON(!protocol || protocol->count)) {
115                 spin_unlock_irq(&gb_protocols_lock);
116                 return;
117         }
118
119         list_del(&protocol->links);
120         spin_unlock_irq(&gb_protocols_lock);
121
122         pr_info("Deregistered %s protocol.\n", protocol->name);
123 }
124 EXPORT_SYMBOL_GPL(gb_protocol_deregister);
125
126 /* Returns the requested protocol if available, or a null pointer */
127 struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor)
128 {
129         struct gb_protocol *protocol;
130         u8 protocol_count;
131
132         spin_lock_irq(&gb_protocols_lock);
133         protocol = gb_protocol_find(id, major, minor);
134         if (protocol) {
135                 if (!try_module_get(protocol->owner)) {
136                         protocol = NULL;
137                 } else {
138                         protocol_count = protocol->count;
139                         if (protocol_count != U8_MAX)
140                                 protocol->count++;
141                 }
142         }
143         spin_unlock_irq(&gb_protocols_lock);
144
145         if (protocol)
146                 WARN_ON(protocol_count == U8_MAX);
147
148         return protocol;
149 }
150
151 int gb_protocol_get_version(struct gb_connection *connection)
152 {
153         struct gb_protocol_version_request request;
154         struct gb_protocol_version_response response;
155         struct gb_protocol *protocol = connection->protocol;
156         int retval;
157
158         request.major = protocol->major;
159         request.minor = protocol->minor;
160
161         retval = gb_operation_sync(connection, GB_REQUEST_TYPE_PROTOCOL_VERSION,
162                                    &request, sizeof(request), &response,
163                                    sizeof(response));
164         if (retval)
165                 return retval;
166
167         if (response.major > connection->protocol->major) {
168                 dev_err(&connection->dev,
169                         "unsupported major version (%hhu > %hhu)\n",
170                         response.major, connection->protocol->major);
171                 return -ENOTSUPP;
172         }
173
174         connection->module_major = response.major;
175         connection->module_minor = response.minor;
176
177
178         dev_dbg(&connection->dev, "%s - %s (0x%02hhx) v%hhu.%hhu\n", __func__,
179                         protocol->name, protocol->id,
180                         response.major, response.minor);
181
182         return 0;
183 }
184 EXPORT_SYMBOL_GPL(gb_protocol_get_version);
185
186 void gb_protocol_put(struct gb_protocol *protocol)
187 {
188         u8 id;
189         u8 major;
190         u8 minor;
191
192         id = protocol->id;
193         major = protocol->major;
194         minor = protocol->minor;
195
196         spin_lock_irq(&gb_protocols_lock);
197         protocol = gb_protocol_find(id, major, minor);
198         if (WARN_ON(!protocol || !protocol->count))
199                 goto out;
200
201         protocol->count--;
202         module_put(protocol->owner);
203 out:
204         spin_unlock_irq(&gb_protocols_lock);
205 }