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