Commit | Line | Data |
---|---|---|
bac8a4d5 KC |
1 | /* uisutils.c |
2 | * | |
f6d0c1e6 | 3 | * Copyright (C) 2010 - 2013 UNISYS CORPORATION |
bac8a4d5 KC |
4 | * All rights reserved. |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or (at | |
9 | * your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | |
14 | * NON INFRINGEMENT. See the GNU General Public License for more | |
15 | * details. | |
16 | */ | |
17 | ||
18 | #include <linux/string.h> | |
19 | #include <linux/slab.h> | |
20 | #include <commontypes.h> | |
21 | #include <linux/spinlock.h> | |
22 | #include <linux/list.h> | |
23 | #include "uniklog.h" | |
24 | #include "uisutils.h" | |
25 | #include "version.h" | |
26 | #include "vbushelper.h" | |
90addb02 | 27 | #include <linux/uuid.h> |
bac8a4d5 | 28 | #include <linux/skbuff.h> |
90addb02 | 29 | #include <linux/uuid.h> |
bac8a4d5 KC |
30 | #ifdef CONFIG_HIGHMEM |
31 | #include <linux/highmem.h> | |
32 | #endif | |
33 | ||
34 | /* this is shorter than using __FILE__ (full path name) in | |
35 | * debug/info/error messages | |
36 | */ | |
37 | #define CURRENT_FILE_PC UISLIB_PC_uisutils_c | |
38 | #define __MYFILE__ "uisutils.c" | |
39 | ||
40 | /* exports */ | |
41 | atomic_t UisUtils_Registered_Services = ATOMIC_INIT(0); | |
42 | /* num registrations via | |
43 | * uisctrl_register_req_handler() or | |
44 | * uisctrl_register_req_handler_ex() */ | |
45 | ||
46 | ||
47 | /*****************************************************/ | |
48 | /* Utility functions */ | |
49 | /*****************************************************/ | |
50 | ||
51 | int | |
927c7927 | 52 | uisutil_add_proc_line_ex(int *total, char **buffer, int *buffer_remaining, |
bac8a4d5 KC |
53 | char *format, ...) |
54 | { | |
55 | va_list args; | |
56 | int len; | |
57 | ||
58 | DBGINF("buffer = 0x%p : *buffer = 0x%p.\n", buffer, *buffer); | |
59 | va_start(args, format); | |
60 | len = vsnprintf(*buffer, *buffer_remaining, format, args); | |
61 | if (len >= *buffer_remaining) { | |
62 | *buffer += *buffer_remaining; | |
63 | *total += *buffer_remaining; | |
64 | *buffer_remaining = 0; | |
65 | LOGERR("bytes remaining is too small!\n"); | |
66 | return -1; | |
67 | } | |
68 | *buffer_remaining -= len; | |
69 | *buffer += len; | |
70 | *total += len; | |
71 | return len; | |
72 | } | |
927c7927 | 73 | EXPORT_SYMBOL_GPL(uisutil_add_proc_line_ex); |
bac8a4d5 KC |
74 | |
75 | int | |
76 | uisctrl_register_req_handler(int type, void *fptr, | |
77 | ULTRA_VBUS_DEVICEINFO *chipset_DriverInfo) | |
78 | { | |
79 | LOGINF("type = %d, fptr = 0x%p.\n", type, fptr); | |
80 | ||
81 | switch (type) { | |
82 | case 2: | |
83 | if (fptr) { | |
84 | if (!VirtControlChanFunc) | |
85 | atomic_inc(&UisUtils_Registered_Services); | |
86 | VirtControlChanFunc = fptr; | |
87 | } else { | |
88 | if (VirtControlChanFunc) | |
89 | atomic_dec(&UisUtils_Registered_Services); | |
90 | VirtControlChanFunc = NULL; | |
91 | } | |
92 | break; | |
93 | ||
94 | default: | |
95 | LOGERR("invalid type %d.\n", type); | |
96 | return 0; | |
97 | } | |
98 | if (chipset_DriverInfo) | |
836bee9e KC |
99 | BusDeviceInfo_Init(chipset_DriverInfo, "chipset", "uislib", |
100 | VERSION, NULL); | |
bac8a4d5 KC |
101 | |
102 | return 1; | |
103 | } | |
104 | EXPORT_SYMBOL_GPL(uisctrl_register_req_handler); | |
105 | ||
106 | int | |
90addb02 | 107 | uisctrl_register_req_handler_ex(uuid_le switchTypeGuid, |
bac8a4d5 KC |
108 | const char *switch_type_name, |
109 | int (*controlfunc)(struct io_msgs *), | |
110 | unsigned long min_channel_bytes, | |
111 | int (*Server_Channel_Ok)(unsigned long | |
112 | channelBytes), | |
113 | int (*Server_Channel_Init) | |
114 | (void *x, unsigned char *clientStr, | |
b3c55b13 | 115 | u32 clientStrLen, U64 bytes), |
bac8a4d5 KC |
116 | ULTRA_VBUS_DEVICEINFO *chipset_DriverInfo) |
117 | { | |
bac8a4d5 KC |
118 | ReqHandlerInfo_t *pReqHandlerInfo; |
119 | int rc = 0; /* assume failure */ | |
90addb02 BR |
120 | LOGINF("type=%pUL, controlfunc=0x%p.\n", |
121 | &switchTypeGuid, controlfunc); | |
bac8a4d5 | 122 | if (!controlfunc) { |
90addb02 | 123 | LOGERR("%pUL: controlfunc must be supplied\n", &switchTypeGuid); |
bac8a4d5 KC |
124 | goto Away; |
125 | } | |
126 | if (!Server_Channel_Ok) { | |
90addb02 BR |
127 | LOGERR("%pUL: Server_Channel_Ok must be supplied\n", |
128 | &switchTypeGuid); | |
bac8a4d5 KC |
129 | goto Away; |
130 | } | |
131 | if (!Server_Channel_Init) { | |
90addb02 BR |
132 | LOGERR("%pUL: Server_Channel_Init must be supplied\n", |
133 | &switchTypeGuid); | |
bac8a4d5 KC |
134 | goto Away; |
135 | } | |
136 | pReqHandlerInfo = ReqHandlerAdd(switchTypeGuid, | |
137 | switch_type_name, | |
138 | controlfunc, | |
139 | min_channel_bytes, | |
140 | Server_Channel_Ok, Server_Channel_Init); | |
141 | if (!pReqHandlerInfo) { | |
90addb02 | 142 | LOGERR("failed to add %pUL to server list\n", &switchTypeGuid); |
bac8a4d5 KC |
143 | goto Away; |
144 | } | |
145 | ||
146 | atomic_inc(&UisUtils_Registered_Services); | |
147 | rc = 1; /* success */ | |
148 | Away: | |
149 | if (rc) { | |
150 | if (chipset_DriverInfo) | |
836bee9e KC |
151 | BusDeviceInfo_Init(chipset_DriverInfo, "chipset", |
152 | "uislib", VERSION, NULL); | |
bac8a4d5 | 153 | } else |
90addb02 | 154 | LOGERR("failed to register type %pUL.\n", &switchTypeGuid); |
bac8a4d5 KC |
155 | |
156 | return rc; | |
157 | } | |
158 | EXPORT_SYMBOL_GPL(uisctrl_register_req_handler_ex); | |
159 | ||
160 | int | |
90addb02 | 161 | uisctrl_unregister_req_handler_ex(uuid_le switchTypeGuid) |
bac8a4d5 | 162 | { |
bac8a4d5 | 163 | int rc = 0; /* assume failure */ |
90addb02 | 164 | LOGINF("type=%pUL.\n", &switchTypeGuid); |
bac8a4d5 | 165 | if (ReqHandlerDel(switchTypeGuid) < 0) { |
90addb02 BR |
166 | LOGERR("failed to remove %pUL from server list\n", |
167 | &switchTypeGuid); | |
bac8a4d5 KC |
168 | goto Away; |
169 | } | |
170 | atomic_dec(&UisUtils_Registered_Services); | |
171 | rc = 1; /* success */ | |
172 | Away: | |
173 | if (!rc) | |
90addb02 | 174 | LOGERR("failed to unregister type %pUL.\n", &switchTypeGuid); |
bac8a4d5 KC |
175 | return rc; |
176 | } | |
177 | EXPORT_SYMBOL_GPL(uisctrl_unregister_req_handler_ex); | |
178 | ||
179 | /* | |
927c7927 | 180 | * unsigned int uisutil_copy_fragsinfo_from_skb(unsigned char *calling_ctx, |
bac8a4d5 KC |
181 | * void *skb_in, |
182 | * unsigned int firstfraglen, | |
183 | * unsigned int frags_max, | |
184 | * struct phys_info frags[]) | |
185 | * | |
186 | * calling_ctx - input - a string that is displayed to show | |
187 | * who called * this func | |
188 | * void *skb_in - skb whose frag info we're copying type is hidden so we | |
189 | * don't need to include skbbuff in uisutils.h which is | |
190 | * included in non-networking code. | |
191 | * unsigned int firstfraglen - input - length of first fragment in skb | |
192 | * unsigned int frags_max - input - max len of frags array | |
193 | * struct phys_info frags[] - output - frags array filled in on output | |
194 | * return value indicates number of | |
195 | * entries filled in frags | |
196 | */ | |
197 | unsigned int | |
927c7927 KC |
198 | uisutil_copy_fragsinfo_from_skb(unsigned char *calling_ctx, void *skb_in, |
199 | unsigned int firstfraglen, | |
200 | unsigned int frags_max, | |
201 | struct phys_info frags[]) | |
bac8a4d5 KC |
202 | { |
203 | unsigned int count = 0, ii, size, offset = 0, numfrags; | |
204 | struct sk_buff *skb = skb_in; | |
205 | ||
206 | numfrags = skb_shinfo(skb)->nr_frags; | |
207 | ||
208 | while (firstfraglen) { | |
209 | if (count == frags_max) { | |
210 | LOGERR("%s frags array too small: max:%d count:%d\n", | |
211 | calling_ctx, frags_max, count); | |
212 | return -1; /* failure */ | |
213 | } | |
214 | frags[count].pi_pfn = | |
215 | page_to_pfn(virt_to_page(skb->data + offset)); | |
216 | frags[count].pi_off = | |
217 | (unsigned long) (skb->data + offset) & PI_PAGE_MASK; | |
218 | size = | |
219 | min(firstfraglen, | |
220 | (unsigned int) (PI_PAGE_SIZE - frags[count].pi_off)); | |
221 | /* can take smallest of firstfraglen(what's left) OR | |
222 | * bytes left in the page | |
223 | */ | |
224 | frags[count].pi_len = size; | |
225 | firstfraglen -= size; | |
226 | offset += size; | |
227 | count++; | |
228 | } | |
229 | if (numfrags) { | |
230 | if ((count + numfrags) > frags_max) { | |
231 | LOGERR("**** FAILED %s frags array too small: max:%d count+nr_frags:%d\n", | |
232 | calling_ctx, frags_max, count + numfrags); | |
233 | return -1; /* failure */ | |
234 | } | |
235 | ||
236 | for (ii = 0; ii < numfrags; ii++) { | |
237 | count = add_physinfo_entries(page_to_pfn(skb_frag_page(&skb_shinfo(skb)->frags[ii])), /* pfn */ | |
238 | skb_shinfo(skb)->frags[ii]. | |
239 | page_offset, | |
240 | skb_shinfo(skb)->frags[ii]. | |
241 | size, count, frags_max, | |
242 | frags); | |
243 | if (count == 0) { | |
244 | LOGERR("**** FAILED to add physinfo entries\n"); | |
245 | return -1; /* failure */ | |
246 | } | |
247 | } | |
248 | } | |
249 | if (skb_shinfo(skb)->frag_list) { | |
250 | struct sk_buff *skbinlist; | |
251 | int c; | |
252 | for (skbinlist = skb_shinfo(skb)->frag_list; skbinlist; | |
253 | skbinlist = skbinlist->next) { | |
254 | ||
927c7927 KC |
255 | c = uisutil_copy_fragsinfo_from_skb("recursive", |
256 | skbinlist, | |
257 | skbinlist->len - | |
258 | skbinlist->data_len, | |
259 | frags_max - count, | |
260 | &frags[count]); | |
bac8a4d5 KC |
261 | if (c == -1) { |
262 | LOGERR("**** FAILED recursive call failed\n"); | |
263 | return -1; | |
264 | } | |
265 | count += c; | |
266 | } | |
267 | } | |
268 | return count; | |
269 | } | |
927c7927 | 270 | EXPORT_SYMBOL_GPL(uisutil_copy_fragsinfo_from_skb); |
bac8a4d5 KC |
271 | |
272 | static LIST_HEAD(ReqHandlerInfo_list); /* list of ReqHandlerInfo_t */ | |
273 | static DEFINE_SPINLOCK(ReqHandlerInfo_list_lock); | |
274 | ||
275 | ReqHandlerInfo_t * | |
90addb02 | 276 | ReqHandlerAdd(uuid_le switchTypeGuid, |
bac8a4d5 KC |
277 | const char *switch_type_name, |
278 | int (*controlfunc)(struct io_msgs *), | |
279 | unsigned long min_channel_bytes, | |
280 | int (*Server_Channel_Ok)(unsigned long channelBytes), | |
281 | int (*Server_Channel_Init) | |
b3c55b13 | 282 | (void *x, unsigned char *clientStr, u32 clientStrLen, U64 bytes)) |
bac8a4d5 KC |
283 | { |
284 | ReqHandlerInfo_t *rc = NULL; | |
285 | ||
60140462 | 286 | rc = kzalloc(sizeof(*rc), GFP_ATOMIC); |
bac8a4d5 KC |
287 | if (!rc) |
288 | return NULL; | |
bac8a4d5 KC |
289 | rc->switchTypeGuid = switchTypeGuid; |
290 | rc->controlfunc = controlfunc; | |
291 | rc->min_channel_bytes = min_channel_bytes; | |
292 | rc->Server_Channel_Ok = Server_Channel_Ok; | |
293 | rc->Server_Channel_Init = Server_Channel_Init; | |
294 | if (switch_type_name) | |
295 | strncpy(rc->switch_type_name, switch_type_name, | |
296 | sizeof(rc->switch_type_name) - 1); | |
297 | spin_lock(&ReqHandlerInfo_list_lock); | |
298 | list_add_tail(&(rc->list_link), &ReqHandlerInfo_list); | |
299 | spin_unlock(&ReqHandlerInfo_list_lock); | |
300 | ||
301 | return rc; | |
302 | } | |
303 | ||
304 | ReqHandlerInfo_t * | |
90addb02 | 305 | ReqHandlerFind(uuid_le switchTypeGuid) |
bac8a4d5 KC |
306 | { |
307 | struct list_head *lelt, *tmp; | |
308 | ReqHandlerInfo_t *entry = NULL; | |
309 | spin_lock(&ReqHandlerInfo_list_lock); | |
310 | list_for_each_safe(lelt, tmp, &ReqHandlerInfo_list) { | |
311 | entry = list_entry(lelt, ReqHandlerInfo_t, list_link); | |
90addb02 | 312 | if (uuid_le_cmp(entry->switchTypeGuid, switchTypeGuid) == 0) { |
bac8a4d5 KC |
313 | spin_unlock(&ReqHandlerInfo_list_lock); |
314 | return entry; | |
315 | } | |
316 | } | |
317 | spin_unlock(&ReqHandlerInfo_list_lock); | |
318 | return NULL; | |
319 | } | |
320 | ||
321 | int | |
90addb02 | 322 | ReqHandlerDel(uuid_le switchTypeGuid) |
bac8a4d5 KC |
323 | { |
324 | struct list_head *lelt, *tmp; | |
325 | ReqHandlerInfo_t *entry = NULL; | |
326 | int rc = -1; | |
327 | spin_lock(&ReqHandlerInfo_list_lock); | |
328 | list_for_each_safe(lelt, tmp, &ReqHandlerInfo_list) { | |
329 | entry = list_entry(lelt, ReqHandlerInfo_t, list_link); | |
90addb02 | 330 | if (uuid_le_cmp(entry->switchTypeGuid, switchTypeGuid) == 0) { |
bac8a4d5 | 331 | list_del(lelt); |
60140462 | 332 | kfree(entry); |
bac8a4d5 KC |
333 | rc++; |
334 | } | |
335 | } | |
336 | spin_unlock(&ReqHandlerInfo_list_lock); | |
337 | return rc; | |
338 | } |