uisutils.c revision 90addb0218d47a886c846a22d75979fe5bf3471b
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 */
41atomic_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
51int
52uisutil_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}
73EXPORT_SYMBOL_GPL(uisutil_add_proc_line_ex);
74
75int
76uisctrl_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,
100				   "chipset", "uislib",
101				   VERSION, NULL, __DATE__, __TIME__);
102
103	return 1;
104}
105EXPORT_SYMBOL_GPL(uisctrl_register_req_handler);
106
107int
108uisctrl_register_req_handler_ex(uuid_le switchTypeGuid,
109				const char *switch_type_name,
110				int (*controlfunc)(struct io_msgs *),
111				unsigned long min_channel_bytes,
112				int (*Server_Channel_Ok)(unsigned long
113							  channelBytes),
114				int (*Server_Channel_Init)
115				 (void *x, unsigned char *clientStr,
116				  U32 clientStrLen, U64 bytes),
117				ULTRA_VBUS_DEVICEINFO *chipset_DriverInfo)
118{
119	ReqHandlerInfo_t *pReqHandlerInfo;
120	int rc = 0;		/* assume failure */
121	LOGINF("type=%pUL, controlfunc=0x%p.\n",
122	       &switchTypeGuid, controlfunc);
123	if (!controlfunc) {
124		LOGERR("%pUL: controlfunc must be supplied\n", &switchTypeGuid);
125		goto Away;
126	}
127	if (!Server_Channel_Ok) {
128		LOGERR("%pUL: Server_Channel_Ok must be supplied\n",
129				&switchTypeGuid);
130		goto Away;
131	}
132	if (!Server_Channel_Init) {
133		LOGERR("%pUL: Server_Channel_Init must be supplied\n",
134				&switchTypeGuid);
135		goto Away;
136	}
137	pReqHandlerInfo = ReqHandlerAdd(switchTypeGuid,
138					switch_type_name,
139					controlfunc,
140					min_channel_bytes,
141					Server_Channel_Ok, Server_Channel_Init);
142	if (!pReqHandlerInfo) {
143		LOGERR("failed to add %pUL to server list\n", &switchTypeGuid);
144		goto Away;
145	}
146
147	atomic_inc(&UisUtils_Registered_Services);
148	rc = 1;			/* success */
149Away:
150	if (rc) {
151		if (chipset_DriverInfo)
152			BusDeviceInfo_Init(chipset_DriverInfo,
153					   "chipset", "uislib",
154					   VERSION, NULL,
155					   __DATE__, __TIME__);
156	} else
157		LOGERR("failed to register type %pUL.\n", &switchTypeGuid);
158
159	return rc;
160}
161EXPORT_SYMBOL_GPL(uisctrl_register_req_handler_ex);
162
163int
164uisctrl_unregister_req_handler_ex(uuid_le switchTypeGuid)
165{
166	int rc = 0;		/* assume failure */
167	LOGINF("type=%pUL.\n", &switchTypeGuid);
168	if (ReqHandlerDel(switchTypeGuid) < 0) {
169		LOGERR("failed to remove %pUL from server list\n",
170				&switchTypeGuid);
171		goto Away;
172	}
173	atomic_dec(&UisUtils_Registered_Services);
174	rc = 1;			/* success */
175Away:
176	if (!rc)
177		LOGERR("failed to unregister type %pUL.\n", &switchTypeGuid);
178	return rc;
179}
180EXPORT_SYMBOL_GPL(uisctrl_unregister_req_handler_ex);
181
182/*
183 * unsigned int uisutil_copy_fragsinfo_from_skb(unsigned char *calling_ctx,
184 *					     void *skb_in,
185 *					     unsigned int firstfraglen,
186 *					     unsigned int frags_max,
187 *					     struct phys_info frags[])
188 *
189 *	calling_ctx - input -   a string that is displayed to show
190 *				who called * this func
191 *	void *skb_in -  skb whose frag info we're copying type is hidden so we
192 *			don't need to include skbbuff in uisutils.h which is
193 *			included in non-networking code.
194 *	unsigned int firstfraglen - input - length of first fragment in skb
195 *	unsigned int frags_max - input - max len of frags array
196 *	struct phys_info frags[] - output - frags array filled in on output
197 *					    return value indicates number of
198 *					    entries filled in frags
199 */
200unsigned int
201uisutil_copy_fragsinfo_from_skb(unsigned char *calling_ctx, void *skb_in,
202				unsigned int firstfraglen,
203				unsigned int frags_max,
204				struct phys_info frags[])
205{
206	unsigned int count = 0, ii, size, offset = 0, numfrags;
207	struct sk_buff *skb = skb_in;
208
209	numfrags = skb_shinfo(skb)->nr_frags;
210
211	while (firstfraglen) {
212		if (count == frags_max) {
213			LOGERR("%s frags array too small: max:%d count:%d\n",
214			       calling_ctx, frags_max, count);
215			return -1;	/* failure */
216		}
217		frags[count].pi_pfn =
218		    page_to_pfn(virt_to_page(skb->data + offset));
219		frags[count].pi_off =
220		    (unsigned long) (skb->data + offset) & PI_PAGE_MASK;
221		size =
222		    min(firstfraglen,
223			(unsigned int) (PI_PAGE_SIZE - frags[count].pi_off));
224		/* can take smallest of firstfraglen(what's left) OR
225		* bytes left in the page
226		*/
227		frags[count].pi_len = size;
228		firstfraglen -= size;
229		offset += size;
230		count++;
231	}
232	if (numfrags) {
233		if ((count + numfrags) > frags_max) {
234			LOGERR("**** FAILED %s frags array too small: max:%d count+nr_frags:%d\n",
235			     calling_ctx, frags_max, count + numfrags);
236			return -1;	/* failure */
237		}
238
239		for (ii = 0; ii < numfrags; ii++) {
240			count = add_physinfo_entries(page_to_pfn(skb_frag_page(&skb_shinfo(skb)->frags[ii])),	/* pfn */
241						     skb_shinfo(skb)->frags[ii].
242						     page_offset,
243						     skb_shinfo(skb)->frags[ii].
244						     size, count, frags_max,
245						     frags);
246			if (count == 0) {
247				LOGERR("**** FAILED to add physinfo entries\n");
248				return -1;	/* failure */
249			}
250		}
251	}
252	if (skb_shinfo(skb)->frag_list) {
253		struct sk_buff *skbinlist;
254		int c;
255		for (skbinlist = skb_shinfo(skb)->frag_list; skbinlist;
256		     skbinlist = skbinlist->next) {
257
258			c = uisutil_copy_fragsinfo_from_skb("recursive",
259							    skbinlist,
260							    skbinlist->len -
261							    skbinlist->data_len,
262							    frags_max - count,
263							    &frags[count]);
264			if (c == -1) {
265				LOGERR("**** FAILED recursive call failed\n");
266				return -1;
267			}
268			count += c;
269		}
270	}
271	return count;
272}
273EXPORT_SYMBOL_GPL(uisutil_copy_fragsinfo_from_skb);
274
275static LIST_HEAD(ReqHandlerInfo_list);	/* list of ReqHandlerInfo_t */
276static DEFINE_SPINLOCK(ReqHandlerInfo_list_lock);
277
278ReqHandlerInfo_t *
279ReqHandlerAdd(uuid_le switchTypeGuid,
280	      const char *switch_type_name,
281	      int (*controlfunc)(struct io_msgs *),
282	      unsigned long min_channel_bytes,
283	      int (*Server_Channel_Ok)(unsigned long channelBytes),
284	      int (*Server_Channel_Init)
285	       (void *x, unsigned char *clientStr, U32 clientStrLen, U64 bytes))
286{
287	ReqHandlerInfo_t *rc = NULL;
288
289	rc = kzalloc(sizeof(*rc), GFP_ATOMIC);
290	if (!rc)
291		return NULL;
292	rc->switchTypeGuid = switchTypeGuid;
293	rc->controlfunc = controlfunc;
294	rc->min_channel_bytes = min_channel_bytes;
295	rc->Server_Channel_Ok = Server_Channel_Ok;
296	rc->Server_Channel_Init = Server_Channel_Init;
297	if (switch_type_name)
298		strncpy(rc->switch_type_name, switch_type_name,
299			sizeof(rc->switch_type_name) - 1);
300	spin_lock(&ReqHandlerInfo_list_lock);
301	list_add_tail(&(rc->list_link), &ReqHandlerInfo_list);
302	spin_unlock(&ReqHandlerInfo_list_lock);
303
304	return rc;
305}
306
307ReqHandlerInfo_t *
308ReqHandlerFind(uuid_le switchTypeGuid)
309{
310	struct list_head *lelt, *tmp;
311	ReqHandlerInfo_t *entry = NULL;
312	spin_lock(&ReqHandlerInfo_list_lock);
313	list_for_each_safe(lelt, tmp, &ReqHandlerInfo_list) {
314		entry = list_entry(lelt, ReqHandlerInfo_t, list_link);
315		if (uuid_le_cmp(entry->switchTypeGuid, switchTypeGuid) == 0) {
316			spin_unlock(&ReqHandlerInfo_list_lock);
317			return entry;
318		}
319	}
320	spin_unlock(&ReqHandlerInfo_list_lock);
321	return NULL;
322}
323
324int
325ReqHandlerDel(uuid_le switchTypeGuid)
326{
327	struct list_head *lelt, *tmp;
328	ReqHandlerInfo_t *entry = NULL;
329	int rc = -1;
330	spin_lock(&ReqHandlerInfo_list_lock);
331	list_for_each_safe(lelt, tmp, &ReqHandlerInfo_list) {
332		entry = list_entry(lelt, ReqHandlerInfo_t, list_link);
333		if (uuid_le_cmp(entry->switchTypeGuid, switchTypeGuid) == 0) {
334			list_del(lelt);
335			kfree(entry);
336			rc++;
337		}
338	}
339	spin_unlock(&ReqHandlerInfo_list_lock);
340	return rc;
341}
342