ee26e009b400674fd94c2073df01a3c34afe0254
[pandora-kernel.git] / drivers / staging / unisys / uislib / uisutils.c
1 /* uisutils.c
2  *
3  * Copyright (C) 2010 - 2013 UNISYS CORPORATION
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"
27 #include <linux/uuid.h>
28 #include <linux/skbuff.h>
29 #include <linux/uuid.h>
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
52 uisutil_add_proc_line_ex(int *total, char **buffer, int *buffer_remaining,
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 }
73 EXPORT_SYMBOL_GPL(uisutil_add_proc_line_ex);
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)
99                 BusDeviceInfo_Init(chipset_DriverInfo, "chipset", "uislib",
100                                    VERSION, NULL);
101
102         return 1;
103 }
104 EXPORT_SYMBOL_GPL(uisctrl_register_req_handler);
105
106 int
107 uisctrl_register_req_handler_ex(uuid_le switchTypeGuid,
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,
115                                   u32 clientStrLen, u64 bytes),
116                                 ULTRA_VBUS_DEVICEINFO *chipset_DriverInfo)
117 {
118         ReqHandlerInfo_t *pReqHandlerInfo;
119         int rc = 0;             /* assume failure */
120         LOGINF("type=%pUL, controlfunc=0x%p.\n",
121                &switchTypeGuid, controlfunc);
122         if (!controlfunc) {
123                 LOGERR("%pUL: controlfunc must be supplied\n", &switchTypeGuid);
124                 goto Away;
125         }
126         if (!Server_Channel_Ok) {
127                 LOGERR("%pUL: Server_Channel_Ok must be supplied\n",
128                                 &switchTypeGuid);
129                 goto Away;
130         }
131         if (!Server_Channel_Init) {
132                 LOGERR("%pUL: Server_Channel_Init must be supplied\n",
133                                 &switchTypeGuid);
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) {
142                 LOGERR("failed to add %pUL to server list\n", &switchTypeGuid);
143                 goto Away;
144         }
145
146         atomic_inc(&UisUtils_Registered_Services);
147         rc = 1;                 /* success */
148 Away:
149         if (rc) {
150                 if (chipset_DriverInfo)
151                         BusDeviceInfo_Init(chipset_DriverInfo, "chipset",
152                                            "uislib", VERSION, NULL);
153         } else
154                 LOGERR("failed to register type %pUL.\n", &switchTypeGuid);
155
156         return rc;
157 }
158 EXPORT_SYMBOL_GPL(uisctrl_register_req_handler_ex);
159
160 int
161 uisctrl_unregister_req_handler_ex(uuid_le switchTypeGuid)
162 {
163         int rc = 0;             /* assume failure */
164         LOGINF("type=%pUL.\n", &switchTypeGuid);
165         if (ReqHandlerDel(switchTypeGuid) < 0) {
166                 LOGERR("failed to remove %pUL from server list\n",
167                                 &switchTypeGuid);
168                 goto Away;
169         }
170         atomic_dec(&UisUtils_Registered_Services);
171         rc = 1;                 /* success */
172 Away:
173         if (!rc)
174                 LOGERR("failed to unregister type %pUL.\n", &switchTypeGuid);
175         return rc;
176 }
177 EXPORT_SYMBOL_GPL(uisctrl_unregister_req_handler_ex);
178
179 /*
180  * unsigned int uisutil_copy_fragsinfo_from_skb(unsigned char *calling_ctx,
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
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[])
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
255                         c = uisutil_copy_fragsinfo_from_skb("recursive",
256                                                             skbinlist,
257                                                             skbinlist->len -
258                                                             skbinlist->data_len,
259                                                             frags_max - count,
260                                                             &frags[count]);
261                         if (c == -1) {
262                                 LOGERR("**** FAILED recursive call failed\n");
263                                 return -1;
264                         }
265                         count += c;
266                 }
267         }
268         return count;
269 }
270 EXPORT_SYMBOL_GPL(uisutil_copy_fragsinfo_from_skb);
271
272 static LIST_HEAD(ReqHandlerInfo_list);  /* list of ReqHandlerInfo_t */
273 static DEFINE_SPINLOCK(ReqHandlerInfo_list_lock);
274
275 ReqHandlerInfo_t *
276 ReqHandlerAdd(uuid_le switchTypeGuid,
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)
282                (void *x, unsigned char *clientStr, u32 clientStrLen, u64 bytes))
283 {
284         ReqHandlerInfo_t *rc = NULL;
285
286         rc = kzalloc(sizeof(*rc), GFP_ATOMIC);
287         if (!rc)
288                 return NULL;
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 *
305 ReqHandlerFind(uuid_le switchTypeGuid)
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);
312                 if (uuid_le_cmp(entry->switchTypeGuid, switchTypeGuid) == 0) {
313                         spin_unlock(&ReqHandlerInfo_list_lock);
314                         return entry;
315                 }
316         }
317         spin_unlock(&ReqHandlerInfo_list_lock);
318         return NULL;
319 }
320
321 int
322 ReqHandlerDel(uuid_le switchTypeGuid)
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);
330                 if (uuid_le_cmp(entry->switchTypeGuid, switchTypeGuid) == 0) {
331                         list_del(lelt);
332                         kfree(entry);
333                         rc++;
334                 }
335         }
336         spin_unlock(&ReqHandlerInfo_list_lock);
337         return rc;
338 }