diff --git a/drivers/staging/unisys/include/visorbus.h b/drivers/staging/unisys/include/visorbus.h index 677627c72c4c..03d56f818a86 100644 --- a/drivers/staging/unisys/include/visorbus.h +++ b/drivers/staging/unisys/include/visorbus.h @@ -166,6 +166,8 @@ struct visor_device { struct controlvm_message_header *pending_msg_hdr; void *vbus_hdr_info; uuid_le partition_uuid; + struct dentry *debugfs_dir; + struct dentry *debugfs_client_bus_info; }; #define to_visor_device(x) container_of(x, struct visor_device, device) diff --git a/drivers/staging/unisys/visorbus/vbuschannel.h b/drivers/staging/unisys/visorbus/vbuschannel.h index e97917522f6a..2409b1558d12 100644 --- a/drivers/staging/unisys/visorbus/vbuschannel.h +++ b/drivers/staging/unisys/visorbus/vbuschannel.h @@ -23,6 +23,7 @@ * the client devices and client drivers for the server end to see. */ #include +#include #include "channel.h" /* {193b331b-c58f-11da-95a9-00e08161165f} */ @@ -72,199 +73,38 @@ struct ultra_vbus_deviceinfo { }; /** - * vbuschannel_sanitize_buffer() - remove non-printable chars from buffer - * @p: destination buffer where chars are written to - * @remain: number of bytes that can be written starting at #p - * @src: pointer to source buffer - * @srcmax: number of valid characters at #src - * - * Reads chars from the buffer at @src for @srcmax bytes, and writes to - * the buffer at @p, which is @remain bytes long, ensuring never to - * overflow the buffer at @p, using the following rules: - * - printable characters are simply copied from the buffer at @src to the - * buffer at @p - * - intervening streaks of non-printable characters in the buffer at @src - * are replaced with a single space in the buffer at @p - * Note that we pay no attention to '\0'-termination. - * - * Pass @p == NULL and @remain == 0 for this special behavior -- In this - * case, we simply return the number of bytes that WOULD HAVE been written - * to a buffer at @p, had it been infinitely big. - * - * Return: the number of bytes written to @p (or WOULD HAVE been written to - * @p, as described in the previous paragraph) - */ -static inline int -vbuschannel_sanitize_buffer(char *p, int remain, char *src, int srcmax) -{ - int chars = 0; - int nonprintable_streak = 0; - - while (srcmax > 0) { - if ((*src >= ' ') && (*src < 0x7f)) { - if (nonprintable_streak) { - if (remain > 0) { - *p = ' '; - p++; - remain--; - chars++; - } else if (!p) { - chars++; - } - nonprintable_streak = 0; - } - if (remain > 0) { - *p = *src; - p++; - remain--; - chars++; - } else if (!p) { - chars++; - } - } else { - nonprintable_streak = 1; - } - src++; - srcmax--; - } - return chars; -} - -#define VBUSCHANNEL_ADDACHAR(ch, p, remain, chars) \ - do { \ - if (remain <= 0) \ - break; \ - *p = ch; \ - p++; chars++; remain--; \ - } while (0) - -/** - * vbuschannel_itoa() - convert non-negative int to string - * @p: destination string - * @remain: max number of bytes that can be written to @p - * @num: input int to convert - * - * Converts the non-negative value at @num to an ascii decimal string - * at @p, writing at most @remain bytes. Note there is NO '\0' termination - * written to @p. - * - * Return: number of bytes written to @p - * - */ -static inline int -vbuschannel_itoa(char *p, int remain, int num) -{ - int digits = 0; - char s[32]; - int i; - - if (num == 0) { - /* '0' is a special case */ - if (remain <= 0) - return 0; - *p = '0'; - return 1; - } - /* form a backwards decimal ascii string in */ - while (num > 0) { - if (digits >= (int)sizeof(s)) - return 0; - s[digits++] = (num % 10) + '0'; - num = num / 10; - } - if (remain < digits) { - /* not enough room left at

to hold number, so fill with - * '?' - */ - for (i = 0; i < remain; i++, p++) - *p = '?'; - return remain; - } - /* plug in the decimal ascii string representing the number, by */ - /* reversing the string we just built in */ - i = digits; - while (i > 0) { - i--; - *p = s[i]; - p++; - } - return digits; -} - -/** - * vbuschannel_devinfo_to_string() - format a struct ultra_vbus_deviceinfo - * to a printable string + * vbuschannel_print_devinfo() - format a struct ultra_vbus_deviceinfo + * and write it to a seq_file * @devinfo: the struct ultra_vbus_deviceinfo to format - * @p: destination string area - * @remain: size of destination string area in bytes + * @seq: seq_file to write to * @devix: the device index to be included in the output data, or -1 if no * device index is to be included * - * Reads @devInfo, and converts its contents to a printable string at @p, - * writing at most @remain bytes. Note there is NO '\0' termination - * written to @p. - * - * Return: number of bytes written to @p + * Reads @devInfo, and writes it in human-readable notation to @seq. */ -static inline int -vbuschannel_devinfo_to_string(struct ultra_vbus_deviceinfo *devinfo, - char *p, int remain, int devix) +static inline void +vbuschannel_print_devinfo(struct ultra_vbus_deviceinfo *devinfo, + struct seq_file *seq, int devix) { - char *psrc; - int nsrc, x, i, pad; - int chars = 0; + if (!isprint(devinfo->devtype[0])) + return; /* uninitialized vbus device entry */ - psrc = &devinfo->devtype[0]; - nsrc = sizeof(devinfo->devtype); - if (vbuschannel_sanitize_buffer(NULL, 0, psrc, nsrc) <= 0) - return 0; + if (devix >= 0) + seq_printf(seq, "[%d]", devix); + else + /* vbus device entry is for bus or chipset */ + seq_puts(seq, " "); - /* emit device index */ - if (devix >= 0) { - VBUSCHANNEL_ADDACHAR('[', p, remain, chars); - x = vbuschannel_itoa(p, remain, devix); - p += x; - remain -= x; - chars += x; - VBUSCHANNEL_ADDACHAR(']', p, remain, chars); - } else { - VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); - VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); - VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); - } - - /* emit device type */ - x = vbuschannel_sanitize_buffer(p, remain, psrc, nsrc); - p += x; - remain -= x; - chars += x; - pad = 15 - x; /* pad device type to be exactly 15 chars */ - for (i = 0; i < pad; i++) - VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); - VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); - - /* emit driver name */ - psrc = &devinfo->drvname[0]; - nsrc = sizeof(devinfo->drvname); - x = vbuschannel_sanitize_buffer(p, remain, psrc, nsrc); - p += x; - remain -= x; - chars += x; - pad = 15 - x; /* pad driver name to be exactly 15 chars */ - for (i = 0; i < pad; i++) - VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); - VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); - - /* emit strings */ - psrc = &devinfo->infostrs[0]; - nsrc = sizeof(devinfo->infostrs); - x = vbuschannel_sanitize_buffer(p, remain, psrc, nsrc); - p += x; - remain -= x; - chars += x; - VBUSCHANNEL_ADDACHAR('\n', p, remain, chars); - - return chars; + /* + * Note: because the s-Par back-end is free to scribble in this area, + * we never assume '\0'-termination. + */ + seq_printf(seq, "%-*.*s ", (int)sizeof(devinfo->devtype), + (int)sizeof(devinfo->devtype), devinfo->devtype); + seq_printf(seq, "%-*.*s ", (int)sizeof(devinfo->drvname), + (int)sizeof(devinfo->drvname), devinfo->drvname); + seq_printf(seq, "%.*s\n", (int)sizeof(devinfo->infostrs), + devinfo->infostrs); } struct spar_vbus_headerinfo { diff --git a/drivers/staging/unisys/visorbus/visorbus_main.c b/drivers/staging/unisys/visorbus/visorbus_main.c index 2b225156d73e..b7001ed69014 100644 --- a/drivers/staging/unisys/visorbus/visorbus_main.c +++ b/drivers/staging/unisys/visorbus/visorbus_main.c @@ -14,6 +14,7 @@ * details. */ +#include #include #include "visorbus.h" @@ -33,6 +34,7 @@ static int visorbus_forcenomatch; #define POLLJIFFIES_NORMALCHANNEL 10 static int busreg_rc = -ENODEV; /* stores the result from bus registration */ +static struct dentry *visorbus_debugfs_dir; /* * DEVICE type attributes @@ -151,6 +153,8 @@ visorbus_release_busdevice(struct device *xdev) { struct visor_device *dev = dev_get_drvdata(xdev); + debugfs_remove(dev->debugfs_client_bus_info); + debugfs_remove_recursive(dev->debugfs_dir); kfree(dev); } @@ -349,70 +353,6 @@ static ssize_t channel_id_show(struct device *dev, } static DEVICE_ATTR_RO(channel_id); -static ssize_t client_bus_info_show(struct device *dev, - struct device_attribute *attr, - char *buf) { - struct visor_device *vdev = to_visor_device(dev); - struct visorchannel *channel = vdev->visorchannel; - - int i, shift, remain = PAGE_SIZE; - unsigned long off; - char *pos = buf; - u8 *partition_name; - struct ultra_vbus_deviceinfo dev_info; - - partition_name = ""; - if (channel) { - if (vdev->name) - partition_name = vdev->name; - shift = snprintf(pos, remain, - "Client device / client driver info for %s partition (vbus #%u):\n", - partition_name, vdev->chipset_bus_no); - pos += shift; - remain -= shift; - shift = visorchannel_read(channel, - offsetof(struct - spar_vbus_channel_protocol, - chp_info), - &dev_info, sizeof(dev_info)); - if (shift >= 0) { - shift = vbuschannel_devinfo_to_string(&dev_info, pos, - remain, -1); - pos += shift; - remain -= shift; - } - shift = visorchannel_read(channel, - offsetof(struct - spar_vbus_channel_protocol, - bus_info), - &dev_info, sizeof(dev_info)); - if (shift >= 0) { - shift = vbuschannel_devinfo_to_string(&dev_info, pos, - remain, -1); - pos += shift; - remain -= shift; - } - off = offsetof(struct spar_vbus_channel_protocol, dev_info); - i = 0; - while (off + sizeof(dev_info) <= - visorchannel_get_nbytes(channel)) { - shift = visorchannel_read(channel, - off, &dev_info, - sizeof(dev_info)); - if (shift >= 0) { - shift = vbuschannel_devinfo_to_string - (&dev_info, pos, remain, i); - pos += shift; - remain -= shift; - } - off += sizeof(dev_info); - i++; - } - } - return PAGE_SIZE - remain; -} -static DEVICE_ATTR_RO(client_bus_info); - static struct attribute *dev_attrs[] = { &dev_attr_partition_handle.attr, &dev_attr_partition_guid.attr, @@ -420,7 +360,6 @@ static struct attribute *dev_attrs[] = { &dev_attr_channel_addr.attr, &dev_attr_channel_bytes.attr, &dev_attr_channel_id.attr, - &dev_attr_client_bus_info.attr, NULL }; @@ -433,6 +372,66 @@ static const struct attribute_group *visorbus_groups[] = { NULL }; +/* + * BUS debugfs entries + * + * define & implement display of debugfs attributes under + * /sys/kernel/debug/visorbus/visorbus. + */ + +static int client_bus_info_debugfs_show(struct seq_file *seq, void *v) +{ + struct visor_device *vdev = seq->private; + struct visorchannel *channel = vdev->visorchannel; + + int i; + unsigned long off; + struct ultra_vbus_deviceinfo dev_info; + + if (!channel) + return 0; + + seq_printf(seq, + "Client device / client driver info for %s partition (vbus #%u):\n", + ((vdev->name) ? (char *)(vdev->name) : ""), + vdev->chipset_bus_no); + if (visorchannel_read(channel, + offsetof(struct spar_vbus_channel_protocol, + chp_info), + &dev_info, sizeof(dev_info)) >= 0) + vbuschannel_print_devinfo(&dev_info, seq, -1); + if (visorchannel_read(channel, + offsetof(struct spar_vbus_channel_protocol, + bus_info), + &dev_info, sizeof(dev_info)) >= 0) + vbuschannel_print_devinfo(&dev_info, seq, -1); + off = offsetof(struct spar_vbus_channel_protocol, dev_info); + i = 0; + while (off + sizeof(dev_info) <= visorchannel_get_nbytes(channel)) { + if (visorchannel_read(channel, off, &dev_info, + sizeof(dev_info)) >= 0) + vbuschannel_print_devinfo(&dev_info, seq, i); + off += sizeof(dev_info); + i++; + } + + return 0; +} + +static int client_bus_info_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, client_bus_info_debugfs_show, + inode->i_private); +} + +static const struct file_operations client_bus_info_debugfs_fops = { + .owner = THIS_MODULE, + .open = client_bus_info_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static void dev_periodic_work(unsigned long __opaque) { @@ -964,6 +963,7 @@ static int create_bus_instance(struct visor_device *dev) { int id = dev->chipset_bus_no; + int err; struct spar_vbus_headerinfo *hdr_info; POSTCODE_LINUX_2(BUS_CREATE_ENTRY_PC, POSTCODE_SEVERITY_INFO); @@ -977,11 +977,26 @@ create_bus_instance(struct visor_device *dev) dev->device.groups = visorbus_groups; dev->device.release = visorbus_release_busdevice; + dev->debugfs_dir = debugfs_create_dir(dev_name(&dev->device), + visorbus_debugfs_dir); + if (!dev->debugfs_dir) { + err = -ENOMEM; + goto err_hdr_info; + } + dev->debugfs_client_bus_info = + debugfs_create_file("client_bus_info", S_IRUSR | S_IRGRP, + dev->debugfs_dir, dev, + &client_bus_info_debugfs_fops); + if (!dev->debugfs_client_bus_info) { + err = -ENOMEM; + goto err_debugfs_dir; + } + if (device_register(&dev->device) < 0) { POSTCODE_LINUX_3(DEVICE_CREATE_FAILURE_PC, id, POSTCODE_SEVERITY_ERR); - kfree(hdr_info); - return -ENODEV; + err = -ENODEV; + goto err_debugfs_created; } if (get_vbus_header_info(dev->visorchannel, hdr_info) >= 0) { @@ -996,6 +1011,16 @@ create_bus_instance(struct visor_device *dev) list_add_tail(&dev->list_all, &list_all_bus_instances); dev_set_drvdata(&dev->device, dev); return 0; + +err_debugfs_created: + debugfs_remove(dev->debugfs_client_bus_info); + +err_debugfs_dir: + debugfs_remove_recursive(dev->debugfs_dir); + +err_hdr_info: + kfree(hdr_info); + return err; } /** @@ -1273,6 +1298,11 @@ visorbus_init(void) int err; POSTCODE_LINUX_3(DRIVER_ENTRY_PC, 0, POSTCODE_SEVERITY_INFO); + + visorbus_debugfs_dir = debugfs_create_dir("visorbus", NULL); + if (!visorbus_debugfs_dir) + return -ENOMEM; + bus_device_info_init(&clientbus_driverinfo, "clientbus", "visorbus"); err = create_bus_type(); @@ -1304,6 +1334,7 @@ visorbus_exit(void) remove_bus_instance(dev); } remove_bus_type(); + debugfs_remove_recursive(visorbus_debugfs_dir); } module_param_named(forcematch, visorbus_forcematch, int, S_IRUGO);