diff options
Diffstat (limited to 'drivers/misc/mei/client.c')
-rw-r--r-- | drivers/misc/mei/client.c | 156 |
1 files changed, 131 insertions, 25 deletions
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 1382d551d7ed..dfbddfe1c7a0 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c | |||
@@ -27,7 +27,63 @@ | |||
27 | #include "client.h" | 27 | #include "client.h" |
28 | 28 | ||
29 | /** | 29 | /** |
30 | * mei_me_cl_init - initialize me client | ||
31 | * | ||
32 | * @me_cl: me client | ||
33 | */ | ||
34 | void mei_me_cl_init(struct mei_me_client *me_cl) | ||
35 | { | ||
36 | INIT_LIST_HEAD(&me_cl->list); | ||
37 | kref_init(&me_cl->refcnt); | ||
38 | } | ||
39 | |||
40 | /** | ||
41 | * mei_me_cl_get - increases me client refcount | ||
42 | * | ||
43 | * @me_cl: me client | ||
44 | * | ||
45 | * Locking: called under "dev->device_lock" lock | ||
46 | * | ||
47 | * Return: me client or NULL | ||
48 | */ | ||
49 | struct mei_me_client *mei_me_cl_get(struct mei_me_client *me_cl) | ||
50 | { | ||
51 | if (me_cl) | ||
52 | kref_get(&me_cl->refcnt); | ||
53 | |||
54 | return me_cl; | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * mei_me_cl_release - unlink and free me client | ||
59 | * | ||
60 | * Locking: called under "dev->device_lock" lock | ||
61 | * | ||
62 | * @ref: me_client refcount | ||
63 | */ | ||
64 | static void mei_me_cl_release(struct kref *ref) | ||
65 | { | ||
66 | struct mei_me_client *me_cl = | ||
67 | container_of(ref, struct mei_me_client, refcnt); | ||
68 | list_del(&me_cl->list); | ||
69 | kfree(me_cl); | ||
70 | } | ||
71 | /** | ||
72 | * mei_me_cl_put - decrease me client refcount and free client if necessary | ||
73 | * | ||
74 | * Locking: called under "dev->device_lock" lock | ||
75 | * | ||
76 | * @me_cl: me client | ||
77 | */ | ||
78 | void mei_me_cl_put(struct mei_me_client *me_cl) | ||
79 | { | ||
80 | if (me_cl) | ||
81 | kref_put(&me_cl->refcnt, mei_me_cl_release); | ||
82 | } | ||
83 | |||
84 | /** | ||
30 | * mei_me_cl_by_uuid - locate me client by uuid | 85 | * mei_me_cl_by_uuid - locate me client by uuid |
86 | * increases ref count | ||
31 | * | 87 | * |
32 | * @dev: mei device | 88 | * @dev: mei device |
33 | * @uuid: me client uuid | 89 | * @uuid: me client uuid |
@@ -43,13 +99,14 @@ struct mei_me_client *mei_me_cl_by_uuid(const struct mei_device *dev, | |||
43 | 99 | ||
44 | list_for_each_entry(me_cl, &dev->me_clients, list) | 100 | list_for_each_entry(me_cl, &dev->me_clients, list) |
45 | if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0) | 101 | if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0) |
46 | return me_cl; | 102 | return mei_me_cl_get(me_cl); |
47 | 103 | ||
48 | return NULL; | 104 | return NULL; |
49 | } | 105 | } |
50 | 106 | ||
51 | /** | 107 | /** |
52 | * mei_me_cl_by_id - locate me client by client id | 108 | * mei_me_cl_by_id - locate me client by client id |
109 | * increases ref count | ||
53 | * | 110 | * |
54 | * @dev: the device structure | 111 | * @dev: the device structure |
55 | * @client_id: me client id | 112 | * @client_id: me client id |
@@ -65,12 +122,14 @@ struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id) | |||
65 | 122 | ||
66 | list_for_each_entry(me_cl, &dev->me_clients, list) | 123 | list_for_each_entry(me_cl, &dev->me_clients, list) |
67 | if (me_cl->client_id == client_id) | 124 | if (me_cl->client_id == client_id) |
68 | return me_cl; | 125 | return mei_me_cl_get(me_cl); |
126 | |||
69 | return NULL; | 127 | return NULL; |
70 | } | 128 | } |
71 | 129 | ||
72 | /** | 130 | /** |
73 | * mei_me_cl_by_uuid_id - locate me client by client id and uuid | 131 | * mei_me_cl_by_uuid_id - locate me client by client id and uuid |
132 | * increases ref count | ||
74 | * | 133 | * |
75 | * @dev: the device structure | 134 | * @dev: the device structure |
76 | * @uuid: me client uuid | 135 | * @uuid: me client uuid |
@@ -88,31 +147,67 @@ struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev, | |||
88 | list_for_each_entry(me_cl, &dev->me_clients, list) | 147 | list_for_each_entry(me_cl, &dev->me_clients, list) |
89 | if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0 && | 148 | if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0 && |
90 | me_cl->client_id == client_id) | 149 | me_cl->client_id == client_id) |
91 | return me_cl; | 150 | return mei_me_cl_get(me_cl); |
151 | |||
92 | return NULL; | 152 | return NULL; |
93 | } | 153 | } |
94 | 154 | ||
95 | /** | 155 | /** |
96 | * mei_me_cl_remove - remove me client matching uuid and client_id | 156 | * mei_me_cl_rm_by_uuid - remove all me clients matching uuid |
97 | * | 157 | * |
98 | * @dev: the device structure | 158 | * @dev: the device structure |
99 | * @uuid: me client uuid | 159 | * @uuid: me client uuid |
100 | * @client_id: me client address | 160 | * |
161 | * Locking: called under "dev->device_lock" lock | ||
101 | */ | 162 | */ |
102 | void mei_me_cl_remove(struct mei_device *dev, const uuid_le *uuid, u8 client_id) | 163 | void mei_me_cl_rm_by_uuid(struct mei_device *dev, const uuid_le *uuid) |
103 | { | 164 | { |
104 | struct mei_me_client *me_cl, *next; | 165 | struct mei_me_client *me_cl, *next; |
105 | 166 | ||
167 | dev_dbg(dev->dev, "remove %pUl\n", uuid); | ||
168 | list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) | ||
169 | if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0) | ||
170 | mei_me_cl_put(me_cl); | ||
171 | } | ||
172 | |||
173 | /** | ||
174 | * mei_me_cl_rm_by_uuid_id - remove all me clients matching client id | ||
175 | * | ||
176 | * @dev: the device structure | ||
177 | * @uuid: me client uuid | ||
178 | * @id: me client id | ||
179 | * | ||
180 | * Locking: called under "dev->device_lock" lock | ||
181 | */ | ||
182 | void mei_me_cl_rm_by_uuid_id(struct mei_device *dev, const uuid_le *uuid, u8 id) | ||
183 | { | ||
184 | struct mei_me_client *me_cl, *next; | ||
185 | const uuid_le *pn; | ||
186 | |||
187 | dev_dbg(dev->dev, "remove %pUl %d\n", uuid, id); | ||
106 | list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) { | 188 | list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) { |
107 | if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0 && | 189 | pn = &me_cl->props.protocol_name; |
108 | me_cl->client_id == client_id) { | 190 | if (me_cl->client_id == id && uuid_le_cmp(*uuid, *pn) == 0) |
109 | list_del(&me_cl->list); | 191 | mei_me_cl_put(me_cl); |
110 | kfree(me_cl); | ||
111 | break; | ||
112 | } | ||
113 | } | 192 | } |
114 | } | 193 | } |
115 | 194 | ||
195 | /** | ||
196 | * mei_me_cl_rm_all - remove all me clients | ||
197 | * | ||
198 | * @dev: the device structure | ||
199 | * | ||
200 | * Locking: called under "dev->device_lock" lock | ||
201 | */ | ||
202 | void mei_me_cl_rm_all(struct mei_device *dev) | ||
203 | { | ||
204 | struct mei_me_client *me_cl, *next; | ||
205 | |||
206 | list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) | ||
207 | mei_me_cl_put(me_cl); | ||
208 | } | ||
209 | |||
210 | |||
116 | 211 | ||
117 | /** | 212 | /** |
118 | * mei_cl_cmp_id - tells if the clients are the same | 213 | * mei_cl_cmp_id - tells if the clients are the same |
@@ -695,6 +790,7 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl) | |||
695 | { | 790 | { |
696 | struct mei_device *dev; | 791 | struct mei_device *dev; |
697 | struct mei_me_client *me_cl; | 792 | struct mei_me_client *me_cl; |
793 | int rets = 0; | ||
698 | 794 | ||
699 | if (WARN_ON(!cl || !cl->dev)) | 795 | if (WARN_ON(!cl || !cl->dev)) |
700 | return -EINVAL; | 796 | return -EINVAL; |
@@ -704,18 +800,19 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl) | |||
704 | if (cl->mei_flow_ctrl_creds > 0) | 800 | if (cl->mei_flow_ctrl_creds > 0) |
705 | return 1; | 801 | return 1; |
706 | 802 | ||
707 | me_cl = mei_me_cl_by_id(dev, cl->me_client_id); | 803 | me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id); |
708 | if (!me_cl) { | 804 | if (!me_cl) { |
709 | cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); | 805 | cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); |
710 | return -ENOENT; | 806 | return -ENOENT; |
711 | } | 807 | } |
712 | 808 | ||
713 | if (me_cl->mei_flow_ctrl_creds) { | 809 | if (me_cl->mei_flow_ctrl_creds > 0) { |
810 | rets = 1; | ||
714 | if (WARN_ON(me_cl->props.single_recv_buf == 0)) | 811 | if (WARN_ON(me_cl->props.single_recv_buf == 0)) |
715 | return -EINVAL; | 812 | rets = -EINVAL; |
716 | return 1; | ||
717 | } | 813 | } |
718 | return 0; | 814 | mei_me_cl_put(me_cl); |
815 | return rets; | ||
719 | } | 816 | } |
720 | 817 | ||
721 | /** | 818 | /** |
@@ -732,28 +829,36 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) | |||
732 | { | 829 | { |
733 | struct mei_device *dev; | 830 | struct mei_device *dev; |
734 | struct mei_me_client *me_cl; | 831 | struct mei_me_client *me_cl; |
832 | int rets; | ||
735 | 833 | ||
736 | if (WARN_ON(!cl || !cl->dev)) | 834 | if (WARN_ON(!cl || !cl->dev)) |
737 | return -EINVAL; | 835 | return -EINVAL; |
738 | 836 | ||
739 | dev = cl->dev; | 837 | dev = cl->dev; |
740 | 838 | ||
741 | me_cl = mei_me_cl_by_id(dev, cl->me_client_id); | 839 | me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id); |
742 | if (!me_cl) { | 840 | if (!me_cl) { |
743 | cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); | 841 | cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); |
744 | return -ENOENT; | 842 | return -ENOENT; |
745 | } | 843 | } |
746 | 844 | ||
747 | if (me_cl->props.single_recv_buf) { | 845 | if (me_cl->props.single_recv_buf) { |
748 | if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0)) | 846 | if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0)) { |
749 | return -EINVAL; | 847 | rets = -EINVAL; |
848 | goto out; | ||
849 | } | ||
750 | me_cl->mei_flow_ctrl_creds--; | 850 | me_cl->mei_flow_ctrl_creds--; |
751 | } else { | 851 | } else { |
752 | if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) | 852 | if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) { |
753 | return -EINVAL; | 853 | rets = -EINVAL; |
854 | goto out; | ||
855 | } | ||
754 | cl->mei_flow_ctrl_creds--; | 856 | cl->mei_flow_ctrl_creds--; |
755 | } | 857 | } |
756 | return 0; | 858 | rets = 0; |
859 | out: | ||
860 | mei_me_cl_put(me_cl); | ||
861 | return rets; | ||
757 | } | 862 | } |
758 | 863 | ||
759 | /** | 864 | /** |
@@ -788,6 +893,9 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) | |||
788 | cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); | 893 | cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); |
789 | return -ENOTTY; | 894 | return -ENOTTY; |
790 | } | 895 | } |
896 | /* always allocate at least client max message */ | ||
897 | length = max_t(size_t, length, me_cl->props.max_msg_length); | ||
898 | mei_me_cl_put(me_cl); | ||
791 | 899 | ||
792 | rets = pm_runtime_get(dev->dev); | 900 | rets = pm_runtime_get(dev->dev); |
793 | if (rets < 0 && rets != -EINPROGRESS) { | 901 | if (rets < 0 && rets != -EINPROGRESS) { |
@@ -802,8 +910,6 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) | |||
802 | goto out; | 910 | goto out; |
803 | } | 911 | } |
804 | 912 | ||
805 | /* always allocate at least client max message */ | ||
806 | length = max_t(size_t, length, me_cl->props.max_msg_length); | ||
807 | rets = mei_io_cb_alloc_resp_buf(cb, length); | 913 | rets = mei_io_cb_alloc_resp_buf(cb, length); |
808 | if (rets) | 914 | if (rets) |
809 | goto out; | 915 | goto out; |