diff options
Diffstat (limited to 'drivers/staging/unisys/uislib/uisutils.c')
-rw-r--r-- | drivers/staging/unisys/uislib/uisutils.c | 350 |
1 files changed, 350 insertions, 0 deletions
diff --git a/drivers/staging/unisys/uislib/uisutils.c b/drivers/staging/unisys/uislib/uisutils.c new file mode 100644 index 000000000000..3178f75e1ebe --- /dev/null +++ b/drivers/staging/unisys/uislib/uisutils.c @@ -0,0 +1,350 @@ +/* uisutils.c + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#include <linux/string.h> +#include <linux/slab.h> +#include <commontypes.h> +#include <linux/spinlock.h> +#include <linux/list.h> +#include "uniklog.h" +#include "uisutils.h" +#include "version.h" +#include "vbushelper.h" +#include "guidutils.h" +#include <linux/skbuff.h> +#ifdef CONFIG_HIGHMEM +#include <linux/highmem.h> +#endif + +/* this is shorter than using __FILE__ (full path name) in + * debug/info/error messages + */ +#define CURRENT_FILE_PC UISLIB_PC_uisutils_c +#define __MYFILE__ "uisutils.c" + +/* exports */ +atomic_t UisUtils_Registered_Services = ATOMIC_INIT(0); + /* num registrations via + * uisctrl_register_req_handler() or + * uisctrl_register_req_handler_ex() */ + + +/*****************************************************/ +/* Utility functions */ +/*****************************************************/ + +int +uisutil_add_proc_line_ex(int *total, char **buffer, int *buffer_remaining, + char *format, ...) +{ + va_list args; + int len; + + DBGINF("buffer = 0x%p : *buffer = 0x%p.\n", buffer, *buffer); + va_start(args, format); + len = vsnprintf(*buffer, *buffer_remaining, format, args); + if (len >= *buffer_remaining) { + *buffer += *buffer_remaining; + *total += *buffer_remaining; + *buffer_remaining = 0; + LOGERR("bytes remaining is too small!\n"); + return -1; + } + *buffer_remaining -= len; + *buffer += len; + *total += len; + return len; +} +EXPORT_SYMBOL_GPL(uisutil_add_proc_line_ex); + +int +uisctrl_register_req_handler(int type, void *fptr, + ULTRA_VBUS_DEVICEINFO *chipset_DriverInfo) +{ + LOGINF("type = %d, fptr = 0x%p.\n", type, fptr); + + switch (type) { + case 2: + if (fptr) { + if (!VirtControlChanFunc) + atomic_inc(&UisUtils_Registered_Services); + VirtControlChanFunc = fptr; + } else { + if (VirtControlChanFunc) + atomic_dec(&UisUtils_Registered_Services); + VirtControlChanFunc = NULL; + } + break; + + default: + LOGERR("invalid type %d.\n", type); + return 0; + } + if (chipset_DriverInfo) + BusDeviceInfo_Init(chipset_DriverInfo, + "chipset", "uislib", + VERSION, NULL, __DATE__, __TIME__); + + return 1; +} +EXPORT_SYMBOL_GPL(uisctrl_register_req_handler); + +int +uisctrl_register_req_handler_ex(GUID switchTypeGuid, + const char *switch_type_name, + int (*controlfunc)(struct io_msgs *), + unsigned long min_channel_bytes, + int (*Server_Channel_Ok)(unsigned long + channelBytes), + int (*Server_Channel_Init) + (void *x, unsigned char *clientStr, + U32 clientStrLen, U64 bytes), + ULTRA_VBUS_DEVICEINFO *chipset_DriverInfo) +{ + char s[99]; + ReqHandlerInfo_t *pReqHandlerInfo; + int rc = 0; /* assume failure */ + LOGINF("type=%s, controlfunc=0x%p.\n", + GUID_format1(&switchTypeGuid, s), controlfunc); + if (!controlfunc) { + LOGERR("%s: controlfunc must be supplied\n", + GUID_format1(&switchTypeGuid, s)); + goto Away; + } + if (!Server_Channel_Ok) { + LOGERR("%s: Server_Channel_Ok must be supplied\n", + GUID_format1(&switchTypeGuid, s)); + goto Away; + } + if (!Server_Channel_Init) { + LOGERR("%s: Server_Channel_Init must be supplied\n", + GUID_format1(&switchTypeGuid, s)); + goto Away; + } + pReqHandlerInfo = ReqHandlerAdd(switchTypeGuid, + switch_type_name, + controlfunc, + min_channel_bytes, + Server_Channel_Ok, Server_Channel_Init); + if (!pReqHandlerInfo) { + LOGERR("failed to add %s to server list\n", + GUID_format1(&switchTypeGuid, s)); + goto Away; + } + + atomic_inc(&UisUtils_Registered_Services); + rc = 1; /* success */ +Away: + if (rc) { + if (chipset_DriverInfo) + BusDeviceInfo_Init(chipset_DriverInfo, + "chipset", "uislib", + VERSION, NULL, + __DATE__, __TIME__); + } else + LOGERR("failed to register type %s.\n", + GUID_format1(&switchTypeGuid, s)); + + return rc; +} +EXPORT_SYMBOL_GPL(uisctrl_register_req_handler_ex); + +int +uisctrl_unregister_req_handler_ex(GUID switchTypeGuid) +{ + char s[99]; + int rc = 0; /* assume failure */ + LOGINF("type=%s.\n", GUID_format1(&switchTypeGuid, s)); + if (ReqHandlerDel(switchTypeGuid) < 0) { + LOGERR("failed to remove %s from server list\n", + GUID_format1(&switchTypeGuid, s)); + goto Away; + } + atomic_dec(&UisUtils_Registered_Services); + rc = 1; /* success */ +Away: + if (!rc) + LOGERR("failed to unregister type %s.\n", + GUID_format1(&switchTypeGuid, s)); + return rc; +} +EXPORT_SYMBOL_GPL(uisctrl_unregister_req_handler_ex); + +/* + * unsigned int uisutil_copy_fragsinfo_from_skb(unsigned char *calling_ctx, + * void *skb_in, + * unsigned int firstfraglen, + * unsigned int frags_max, + * struct phys_info frags[]) + * + * calling_ctx - input - a string that is displayed to show + * who called * this func + * void *skb_in - skb whose frag info we're copying type is hidden so we + * don't need to include skbbuff in uisutils.h which is + * included in non-networking code. + * unsigned int firstfraglen - input - length of first fragment in skb + * unsigned int frags_max - input - max len of frags array + * struct phys_info frags[] - output - frags array filled in on output + * return value indicates number of + * entries filled in frags + */ +unsigned int +uisutil_copy_fragsinfo_from_skb(unsigned char *calling_ctx, void *skb_in, + unsigned int firstfraglen, + unsigned int frags_max, + struct phys_info frags[]) +{ + unsigned int count = 0, ii, size, offset = 0, numfrags; + struct sk_buff *skb = skb_in; + + numfrags = skb_shinfo(skb)->nr_frags; + + while (firstfraglen) { + if (count == frags_max) { + LOGERR("%s frags array too small: max:%d count:%d\n", + calling_ctx, frags_max, count); + return -1; /* failure */ + } + frags[count].pi_pfn = + page_to_pfn(virt_to_page(skb->data + offset)); + frags[count].pi_off = + (unsigned long) (skb->data + offset) & PI_PAGE_MASK; + size = + min(firstfraglen, + (unsigned int) (PI_PAGE_SIZE - frags[count].pi_off)); + /* can take smallest of firstfraglen(what's left) OR + * bytes left in the page + */ + frags[count].pi_len = size; + firstfraglen -= size; + offset += size; + count++; + } + if (numfrags) { + if ((count + numfrags) > frags_max) { + LOGERR("**** FAILED %s frags array too small: max:%d count+nr_frags:%d\n", + calling_ctx, frags_max, count + numfrags); + return -1; /* failure */ + } + + for (ii = 0; ii < numfrags; ii++) { + count = add_physinfo_entries(page_to_pfn(skb_frag_page(&skb_shinfo(skb)->frags[ii])), /* pfn */ + skb_shinfo(skb)->frags[ii]. + page_offset, + skb_shinfo(skb)->frags[ii]. + size, count, frags_max, + frags); + if (count == 0) { + LOGERR("**** FAILED to add physinfo entries\n"); + return -1; /* failure */ + } + } + } + if (skb_shinfo(skb)->frag_list) { + struct sk_buff *skbinlist; + int c; + for (skbinlist = skb_shinfo(skb)->frag_list; skbinlist; + skbinlist = skbinlist->next) { + + c = uisutil_copy_fragsinfo_from_skb("recursive", + skbinlist, + skbinlist->len - + skbinlist->data_len, + frags_max - count, + &frags[count]); + if (c == -1) { + LOGERR("**** FAILED recursive call failed\n"); + return -1; + } + count += c; + } + } + return count; +} +EXPORT_SYMBOL_GPL(uisutil_copy_fragsinfo_from_skb); + +static LIST_HEAD(ReqHandlerInfo_list); /* list of ReqHandlerInfo_t */ +static DEFINE_SPINLOCK(ReqHandlerInfo_list_lock); + +ReqHandlerInfo_t * +ReqHandlerAdd(GUID switchTypeGuid, + const char *switch_type_name, + int (*controlfunc)(struct io_msgs *), + unsigned long min_channel_bytes, + int (*Server_Channel_Ok)(unsigned long channelBytes), + int (*Server_Channel_Init) + (void *x, unsigned char *clientStr, U32 clientStrLen, U64 bytes)) +{ + ReqHandlerInfo_t *rc = NULL; + + rc = kzalloc(sizeof(*rc), GFP_ATOMIC); + if (!rc) + return NULL; + rc->switchTypeGuid = switchTypeGuid; + rc->controlfunc = controlfunc; + rc->min_channel_bytes = min_channel_bytes; + rc->Server_Channel_Ok = Server_Channel_Ok; + rc->Server_Channel_Init = Server_Channel_Init; + if (switch_type_name) + strncpy(rc->switch_type_name, switch_type_name, + sizeof(rc->switch_type_name) - 1); + spin_lock(&ReqHandlerInfo_list_lock); + list_add_tail(&(rc->list_link), &ReqHandlerInfo_list); + spin_unlock(&ReqHandlerInfo_list_lock); + + return rc; +} + +ReqHandlerInfo_t * +ReqHandlerFind(GUID switchTypeGuid) +{ + struct list_head *lelt, *tmp; + ReqHandlerInfo_t *entry = NULL; + spin_lock(&ReqHandlerInfo_list_lock); + list_for_each_safe(lelt, tmp, &ReqHandlerInfo_list) { + entry = list_entry(lelt, ReqHandlerInfo_t, list_link); + if (memcmp + (&entry->switchTypeGuid, &switchTypeGuid, + sizeof(GUID)) == 0) { + spin_unlock(&ReqHandlerInfo_list_lock); + return entry; + } + } + spin_unlock(&ReqHandlerInfo_list_lock); + return NULL; +} + +int +ReqHandlerDel(GUID switchTypeGuid) +{ + struct list_head *lelt, *tmp; + ReqHandlerInfo_t *entry = NULL; + int rc = -1; + spin_lock(&ReqHandlerInfo_list_lock); + list_for_each_safe(lelt, tmp, &ReqHandlerInfo_list) { + entry = list_entry(lelt, ReqHandlerInfo_t, list_link); + if (memcmp + (&entry->switchTypeGuid, &switchTypeGuid, + sizeof(GUID)) == 0) { + list_del(lelt); + kfree(entry); + rc++; + } + } + spin_unlock(&ReqHandlerInfo_list_lock); + return rc; +} |