]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/greybus/svc.c
greybus: svc: use macro for init and exit protocol
[karo-tx-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 <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/slab.h>
13
14 #include "greybus.h"
15 #include "greybus_protocols.h"
16
17 struct gb_svc {
18         struct gb_connection    *connection;
19         u8                      version_major;
20         u8                      version_minor;
21 };
22
23 /* Define get_version() routine */
24 define_get_version(gb_svc, SVC);
25
26 static 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
38 static 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
48 static 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
63 static 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
78 int 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 }
82 EXPORT_SYMBOL_GPL(gb_svc_intf_device_id);
83
84 int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id)
85 {
86         return intf_reset_operation(svc, intf_id);
87 }
88 EXPORT_SYMBOL_GPL(gb_svc_intf_reset);
89
90 int 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 }
97 EXPORT_SYMBOL_GPL(gb_svc_connection_create);
98
99 int 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 }
106 EXPORT_SYMBOL_GPL(gb_svc_connection_destroy);
107
108 static 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 = le32_to_cpu(hotplug->data.unipro_mfg_id);
135         unipro_prod_id = le32_to_cpu(hotplug->data.unipro_prod_id);
136         ara_vend_id = le32_to_cpu(hotplug->data.ara_vend_id);
137         ara_prod_id = le32_to_cpu(hotplug->data.ara_prod_id);
138
139         /* FIXME Set up the interface here; may required firmware download */
140
141         return 0;
142 }
143
144 static 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
165 static 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
185 static 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  */
204 static 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
210 static 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
228 static 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
238 static 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
248 gb_gpbridge_protocol_driver(svc_protocol);