greybus: hd: rename host-device structure
[cascardo/linux.git] / drivers / staging / greybus / hd.c
1 /*
2  * Greybus Host Device
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 <linux/kernel.h>
13 #include <linux/slab.h>
14
15 #include "greybus.h"
16
17 static DEFINE_MUTEX(hd_mutex);
18
19
20 static void free_hd(struct kref *kref)
21 {
22         struct gb_host_device *hd;
23
24         hd = container_of(kref, struct gb_host_device, kref);
25
26         ida_destroy(&hd->cport_id_map);
27         kfree(hd);
28         mutex_unlock(&hd_mutex);
29 }
30
31 struct gb_host_device *greybus_create_hd(struct greybus_host_driver *driver,
32                                               struct device *parent,
33                                               size_t buffer_size_max,
34                                               size_t num_cports)
35 {
36         struct gb_host_device *hd;
37
38         /*
39          * Validate that the driver implements all of the callbacks
40          * so that we don't have to every time we make them.
41          */
42         if ((!driver->message_send) || (!driver->message_cancel)) {
43                 pr_err("Must implement all greybus_host_driver callbacks!\n");
44                 return ERR_PTR(-EINVAL);
45         }
46
47         if (buffer_size_max < GB_OPERATION_MESSAGE_SIZE_MIN) {
48                 dev_err(parent, "greybus host-device buffers too small\n");
49                 return ERR_PTR(-EINVAL);
50         }
51
52         if (num_cports == 0 || num_cports > CPORT_ID_MAX) {
53                 dev_err(parent, "Invalid number of CPorts: %zu\n", num_cports);
54                 return ERR_PTR(-EINVAL);
55         }
56
57         /*
58          * Make sure to never allocate messages larger than what the Greybus
59          * protocol supports.
60          */
61         if (buffer_size_max > GB_OPERATION_MESSAGE_SIZE_MAX) {
62                 dev_warn(parent, "limiting buffer size to %u\n",
63                          GB_OPERATION_MESSAGE_SIZE_MAX);
64                 buffer_size_max = GB_OPERATION_MESSAGE_SIZE_MAX;
65         }
66
67         hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL);
68         if (!hd)
69                 return ERR_PTR(-ENOMEM);
70
71         kref_init(&hd->kref);
72         hd->parent = parent;
73         hd->driver = driver;
74         INIT_LIST_HEAD(&hd->interfaces);
75         INIT_LIST_HEAD(&hd->connections);
76         ida_init(&hd->cport_id_map);
77         hd->buffer_size_max = buffer_size_max;
78         hd->num_cports = num_cports;
79
80         /*
81          * Initialize AP's SVC protocol connection:
82          *
83          * This is required as part of early initialization of the host device
84          * as we need this connection in order to start any kind of message
85          * exchange between the AP and the SVC. SVC will start with a
86          * 'get-version' request followed by a 'svc-hello' message and at that
87          * time we will create a fully initialized svc-connection, as we need
88          * endo-id and AP's interface id for that.
89          */
90         if (!gb_ap_svc_connection_create(hd)) {
91                 kref_put_mutex(&hd->kref, free_hd, &hd_mutex);
92                 return ERR_PTR(-ENOMEM);
93         }
94
95         return hd;
96 }
97 EXPORT_SYMBOL_GPL(greybus_create_hd);
98
99 void greybus_remove_hd(struct gb_host_device *hd)
100 {
101         /*
102          * Tear down all interfaces, modules, and the endo that is associated
103          * with this host controller before freeing the memory associated with
104          * the host controller.
105          */
106         gb_interfaces_remove(hd);
107         gb_endo_remove(hd->endo);
108
109         /* Is the SVC still using the partially uninitialized connection ? */
110         if (hd->initial_svc_connection)
111                 gb_connection_destroy(hd->initial_svc_connection);
112
113         kref_put_mutex(&hd->kref, free_hd, &hd_mutex);
114 }
115 EXPORT_SYMBOL_GPL(greybus_remove_hd);