blob: e39eddbbcb9eb429f5ed7e39f85ff8aa91ff6986 [file] [log] [blame]
Alex Elder30c6d9d2015-05-22 13:02:08 -05001/*
2 * SVC Greybus driver.
3 *
4 * Copyright 2015 Google Inc.
5 * Copyright 2015 Linaro Ltd.
6 *
7 * Released under the GPLv2 only.
8 */
9
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/slab.h>
13
14#include "greybus.h"
15#include "greybus_protocols.h"
16
17struct gb_svc {
18 struct gb_connection *connection;
19 u8 version_major;
20 u8 version_minor;
21};
22
23/* Define get_version() routine */
24define_get_version(gb_svc, SVC);
25
26static int intf_device_id_operation(struct gb_svc *svc,
27 u8 intf_id, u8 device_id)
28{
29 struct gb_svc_intf_device_id_request request;
30
31 request.intf_id = intf_id;
32 request.device_id = device_id;
33
34 return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_DEVICE_ID,
35 &request, sizeof(request), NULL, 0);
36}
37
38static int intf_reset_operation(struct gb_svc *svc, u8 intf_id)
39{
40 struct gb_svc_intf_reset_request request;
41
42 request.intf_id = intf_id;
43
44 return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_RESET,
45 &request, sizeof(request), NULL, 0);
46}
47
48static int connection_create_operation(struct gb_svc *svc,
49 u8 intf1_id, u16 cport1_id,
50 u8 intf2_id, u16 cport2_id)
51{
52 struct gb_svc_conn_create_request request;
53
54 request.intf1_id = intf1_id;
55 request.cport1_id = cport1_id;
56 request.intf2_id = intf2_id;
57 request.cport2_id = cport2_id;
58
59 return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE,
60 &request, sizeof(request), NULL, 0);
61}
62
63static int connection_destroy_operation(struct gb_svc *svc,
64 u8 intf1_id, u16 cport1_id,
65 u8 intf2_id, u16 cport2_id)
66{
67 struct gb_svc_conn_destroy_request request;
68
69 request.intf1_id = intf1_id;
70 request.cport1_id = cport1_id;
71 request.intf2_id = intf2_id;
72 request.cport2_id = cport2_id;
73
74 return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_DESTROY,
75 &request, sizeof(request), NULL, 0);
76}
77
78int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id)
79{
80 return intf_device_id_operation(svc, intf_id, device_id);
81}
82EXPORT_SYMBOL_GPL(gb_svc_intf_device_id);
83
84int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id)
85{
86 return intf_reset_operation(svc, intf_id);
87}
88EXPORT_SYMBOL_GPL(gb_svc_intf_reset);
89
90int gb_svc_connection_create(struct gb_svc *svc,
91 u8 intf1_id, u16 cport1_id,
92 u8 intf2_id, u16 cport2_id)
93{
94 return connection_create_operation(svc, intf1_id, cport1_id,
95 intf2_id, cport2_id);
96}
97EXPORT_SYMBOL_GPL(gb_svc_connection_create);
98
99int gb_svc_connection_destroy(struct gb_svc *svc,
100 u8 intf1_id, u16 cport1_id,
101 u8 intf2_id, u16 cport2_id)
102{
103 return connection_destroy_operation(svc, intf1_id, cport1_id,
104 intf2_id, cport2_id);
105}
106EXPORT_SYMBOL_GPL(gb_svc_connection_destroy);
107
108static int gb_svc_intf_hotplug_recv(struct gb_operation *op)
109{
110 struct gb_message *request = op->request;
111 struct gb_svc_intf_hotplug_request *hotplug;
112 u8 intf_id;
113 u32 unipro_mfg_id;
114 u32 unipro_prod_id;
115 u32 ara_vend_id;
116 u32 ara_prod_id;
117
118 if (request->payload_size < sizeof(*hotplug)) {
119 dev_err(&op->connection->dev,
120 "short hotplug request received\n");
121 return -EINVAL;
122 }
123 hotplug = request->payload;
124
125 /*
126 * Grab the information we need.
127 *
128 * XXX I'd really like to acknowledge receipt, and then
129 * XXX continue processing the request. There's no need
130 * XXX for the SVC to wait. In fact, it might be best to
131 * XXX have the SVC get acknowledgement before we proceed.
132 * */
133 intf_id = hotplug->intf_id;
134 unipro_mfg_id = hotplug->data.unipro_mfg_id;
135 unipro_prod_id = hotplug->data.unipro_prod_id;
136 ara_vend_id = hotplug->data.ara_vend_id;
137 ara_prod_id = hotplug->data.ara_prod_id;
138
139 /* FIXME Set up the interface here; may required firmware download */
140
141 return 0;
142}
143
144static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op)
145{
146 struct gb_message *request = op->request;
147 struct gb_svc_intf_hot_unplug_request *hot_unplug;
148 u8 intf_id;
149
150 if (request->payload_size < sizeof(*hot_unplug)) {
151 dev_err(&op->connection->dev,
152 "short hot unplug request received\n");
153 return -EINVAL;
154 }
155 hot_unplug = request->payload;
156
157 intf_id = hot_unplug->intf_id;
158
159 /* FIXME Tear down the interface here */
160
161 return 0;
162
163}
164
165static int gb_svc_intf_reset_recv(struct gb_operation *op)
166{
167 struct gb_message *request = op->request;
168 struct gb_svc_intf_reset_request *reset;
169 u8 intf_id;
170
171 if (request->payload_size < sizeof(*reset)) {
172 dev_err(&op->connection->dev,
173 "short reset request received\n");
174 return -EINVAL;
175 }
176 reset = request->payload;
177
178 intf_id = reset->intf_id;
179
180 /* FIXME Reset the interface here */
181
182 return 0;
183}
184
185static int gb_svc_request_recv(u8 type, struct gb_operation *op)
186{
187 switch (type) {
188 case GB_SVC_TYPE_INTF_HOTPLUG:
189 return gb_svc_intf_hotplug_recv(op);
190 case GB_SVC_TYPE_INTF_HOT_UNPLUG:
191 return gb_svc_intf_hot_unplug_recv(op);
192 case GB_SVC_TYPE_INTF_RESET:
193 return gb_svc_intf_reset_recv(op);
194 default:
195 dev_err(&op->connection->dev,
196 "unsupported request: %hhu\n", type);
197 return -EINVAL;
198 }
199}
200
201/*
202 * Do initial setup of the SVC.
203 */
204static int gb_svc_device_setup(struct gb_svc *gb_svc)
205{
206 /* First thing we need to do is check the version */
207 return get_version(gb_svc);
208}
209
210static int gb_svc_connection_init(struct gb_connection *connection)
211{
212 struct gb_svc *svc;
213 int ret;
214
215 svc = kzalloc(sizeof(*svc), GFP_KERNEL);
216 if (!svc)
217 return -ENOMEM;
218
219 svc->connection = connection;
220 connection->private = svc;
221 ret = gb_svc_device_setup(svc);
222 if (ret)
223 kfree(svc);
224
225 return ret;
226}
227
228static void gb_svc_connection_exit(struct gb_connection *connection)
229{
230 struct gb_svc *svc = connection->private;
231
232 if (!svc)
233 return;
234
235 kfree(svc);
236}
237
238static struct gb_protocol svc_protocol = {
239 .name = "svc",
240 .id = GREYBUS_PROTOCOL_SVC,
241 .major = 0,
242 .minor = 1,
243 .connection_init = gb_svc_connection_init,
244 .connection_exit = gb_svc_connection_exit,
245 .request_recv = gb_svc_request_recv,
246};
247
248int gb_svc_protocol_init(void)
249{
250 return gb_protocol_register(&svc_protocol);
251}
252
253void gb_svc_protocol_exit(void)
254{
255 gb_protocol_deregister(&svc_protocol);
256}