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; |
