diff options
| author | Alexander Shishkin <alexander.shishkin@linux.intel.com> | 2012-05-11 17:25:46 +0300 | 
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-11 16:49:51 -0700 | 
| commit | e443b333629f82ca0da91a05ca638050943bbedd (patch) | |
| tree | d8c2cbf06e7faeef2c062b01271f8987cca23459 /drivers/usb/chipidea/debug.c | |
| parent | bc25a80d12ea971ddd652717150058989b1ad474 (diff) | |
| download | olio-linux-3.10-e443b333629f82ca0da91a05ca638050943bbedd.tar.xz olio-linux-3.10-e443b333629f82ca0da91a05ca638050943bbedd.zip  | |
usb: chipidea: split the driver code into units
Split the driver into the following parts:
  * core  -- resources, register access, capabilities, etc;
  * udc   -- device controller functionality;
  * debug -- logging events.
Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/chipidea/debug.c')
| -rw-r--r-- | drivers/usb/chipidea/debug.c | 804 | 
1 files changed, 804 insertions, 0 deletions
diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c new file mode 100644 index 00000000000..c4b3e15532d --- /dev/null +++ b/drivers/usb/chipidea/debug.c @@ -0,0 +1,804 @@ +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dmapool.h> +#include <linux/dma-mapping.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/pm_runtime.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/otg.h> +#include <linux/usb/chipidea.h> + +#include "ci.h" +#include "udc.h" +#include "bits.h" +#include "debug.h" + +/* Interrupt statistics */ +#define ISR_MASK   0x1F +static struct isr_statistics { +	u32 test; +	u32 ui; +	u32 uei; +	u32 pci; +	u32 uri; +	u32 sli; +	u32 none; +	struct { +		u32 cnt; +		u32 buf[ISR_MASK+1]; +		u32 idx; +	} hndl; +} isr_statistics; + +void dbg_interrupt(u32 intmask) +{ +	if (!intmask) { +		isr_statistics.none++; +		return; +	} + +	isr_statistics.hndl.buf[isr_statistics.hndl.idx++] = intmask; +	isr_statistics.hndl.idx &= ISR_MASK; +	isr_statistics.hndl.cnt++; + +	if (USBi_URI & intmask) +		isr_statistics.uri++; +	if (USBi_PCI & intmask) +		isr_statistics.pci++; +	if (USBi_UEI & intmask) +		isr_statistics.uei++; +	if (USBi_UI  & intmask) +		isr_statistics.ui++; +	if (USBi_SLI & intmask) +		isr_statistics.sli++; +} + +/** + * hw_register_read: reads all device registers (execute without interruption) + * @buf:  destination buffer + * @size: buffer size + * + * This function returns number of registers read + */ +static size_t hw_register_read(struct ci13xxx *udc, u32 *buf, size_t size) +{ +	unsigned i; + +	if (size > udc->hw_bank.size) +		size = udc->hw_bank.size; + +	for (i = 0; i < size; i++) +		buf[i] = hw_read(udc, i * sizeof(u32), ~0); + +	return size; +} + +/** + * hw_register_write: writes to register + * @addr: register address + * @data: register value + * + * This function returns an error code + */ +static int hw_register_write(struct ci13xxx *udc, u16 addr, u32 data) +{ +	/* align */ +	addr /= sizeof(u32); + +	if (addr >= udc->hw_bank.size) +		return -EINVAL; + +	/* align */ +	addr *= sizeof(u32); + +	hw_write(udc, addr, ~0, data); +	return 0; +} + +/** + * hw_intr_clear: disables interrupt & clears interrupt status (execute without + *                interruption) + * @n: interrupt bit + * + * This function returns an error code + */ +static int hw_intr_clear(struct ci13xxx *udc, int n) +{ +	if (n >= REG_BITS) +		return -EINVAL; + +	hw_write(udc, OP_USBINTR, BIT(n), 0); +	hw_write(udc, OP_USBSTS,  BIT(n), BIT(n)); +	return 0; +} + +/** + * hw_intr_force: enables interrupt & forces interrupt status (execute without + *                interruption) + * @n: interrupt bit + * + * This function returns an error code + */ +static int hw_intr_force(struct ci13xxx *udc, int n) +{ +	if (n >= REG_BITS) +		return -EINVAL; + +	hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE); +	hw_write(udc, OP_USBINTR,  BIT(n), BIT(n)); +	hw_write(udc, OP_USBSTS,   BIT(n), BIT(n)); +	hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, 0); +	return 0; +} + +/** + * show_device: prints information about device capabilities and status + * + * Check "device.h" for details + */ +static ssize_t show_device(struct device *dev, struct device_attribute *attr, +			   char *buf) +{ +	struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); +	struct usb_gadget *gadget = &udc->gadget; +	int n = 0; + +	if (attr == NULL || buf == NULL) { +		dev_err(udc->dev, "[%s] EINVAL\n", __func__); +		return 0; +	} + +	n += scnprintf(buf + n, PAGE_SIZE - n, "speed             = %d\n", +		       gadget->speed); +	n += scnprintf(buf + n, PAGE_SIZE - n, "max_speed         = %d\n", +		       gadget->max_speed); +	/* TODO: Scheduled for removal in 3.8. */ +	n += scnprintf(buf + n, PAGE_SIZE - n, "is_dualspeed      = %d\n", +		       gadget_is_dualspeed(gadget)); +	n += scnprintf(buf + n, PAGE_SIZE - n, "is_otg            = %d\n", +		       gadget->is_otg); +	n += scnprintf(buf + n, PAGE_SIZE - n, "is_a_peripheral   = %d\n", +		       gadget->is_a_peripheral); +	n += scnprintf(buf + n, PAGE_SIZE - n, "b_hnp_enable      = %d\n", +		       gadget->b_hnp_enable); +	n += scnprintf(buf + n, PAGE_SIZE - n, "a_hnp_support     = %d\n", +		       gadget->a_hnp_support); +	n += scnprintf(buf + n, PAGE_SIZE - n, "a_alt_hnp_support = %d\n", +		       gadget->a_alt_hnp_support); +	n += scnprintf(buf + n, PAGE_SIZE - n, "name              = %s\n", +		       (gadget->name ? gadget->name : "")); + +	return n; +} +static DEVICE_ATTR(device, S_IRUSR, show_device, NULL); + +/** + * show_driver: prints information about attached gadget (if any) + * + * Check "device.h" for details + */ +static ssize_t show_driver(struct device *dev, struct device_attribute *attr, +			   char *buf) +{ +	struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); +	struct usb_gadget_driver *driver = udc->driver; +	int n = 0; + +	if (attr == NULL || buf == NULL) { +		dev_err(dev, "[%s] EINVAL\n", __func__); +		return 0; +	} + +	if (driver == NULL) +		return scnprintf(buf, PAGE_SIZE, +				 "There is no gadget attached!\n"); + +	n += scnprintf(buf + n, PAGE_SIZE - n, "function  = %s\n", +		       (driver->function ? driver->function : "")); +	n += scnprintf(buf + n, PAGE_SIZE - n, "max speed = %d\n", +		       driver->max_speed); + +	return n; +} +static DEVICE_ATTR(driver, S_IRUSR, show_driver, NULL); + +/* Maximum event message length */ +#define DBG_DATA_MSG   64UL + +/* Maximum event messages */ +#define DBG_DATA_MAX   128UL + +/* Event buffer descriptor */ +static struct { +	char     (buf[DBG_DATA_MAX])[DBG_DATA_MSG];   /* buffer */ +	unsigned idx;   /* index */ +	unsigned tty;   /* print to console? */ +	rwlock_t lck;   /* lock */ +} dbg_data = { +	.idx = 0, +	.tty = 0, +	.lck = __RW_LOCK_UNLOCKED(lck) +}; + +/** + * dbg_dec: decrements debug event index + * @idx: buffer index + */ +static void dbg_dec(unsigned *idx) +{ +	*idx = (*idx - 1) & (DBG_DATA_MAX-1); +} + +/** + * dbg_inc: increments debug event index + * @idx: buffer index + */ +static void dbg_inc(unsigned *idx) +{ +	*idx = (*idx + 1) & (DBG_DATA_MAX-1); +} + +/** + * dbg_print:  prints the common part of the event + * @addr:   endpoint address + * @name:   event name + * @status: status + * @extra:  extra information + */ +static void dbg_print(u8 addr, const char *name, int status, const char *extra) +{ +	struct timeval tval; +	unsigned int stamp; +	unsigned long flags; + +	write_lock_irqsave(&dbg_data.lck, flags); + +	do_gettimeofday(&tval); +	stamp = tval.tv_sec & 0xFFFF;	/* 2^32 = 4294967296. Limit to 4096s */ +	stamp = stamp * 1000000 + tval.tv_usec; + +	scnprintf(dbg_data.buf[dbg_data.idx], DBG_DATA_MSG, +		  "%04X\t? %02X %-7.7s %4i ?\t%s\n", +		  stamp, addr, name, status, extra); + +	dbg_inc(&dbg_data.idx); + +	write_unlock_irqrestore(&dbg_data.lck, flags); + +	if (dbg_data.tty != 0) +		pr_notice("%04X\t? %02X %-7.7s %4i ?\t%s\n", +			  stamp, addr, name, status, extra); +} + +/** + * dbg_done: prints a DONE event + * @addr:   endpoint address + * @td:     transfer descriptor + * @status: status + */ +void dbg_done(u8 addr, const u32 token, int status) +{ +	char msg[DBG_DATA_MSG]; + +	scnprintf(msg, sizeof(msg), "%d %02X", +		  (int)(token & TD_TOTAL_BYTES) >> ffs_nr(TD_TOTAL_BYTES), +		  (int)(token & TD_STATUS)      >> ffs_nr(TD_STATUS)); +	dbg_print(addr, "DONE", status, msg); +} + +/** + * dbg_event: prints a generic event + * @addr:   endpoint address + * @name:   event name + * @status: status + */ +void dbg_event(u8 addr, const char *name, int status) +{ +	if (name != NULL) +		dbg_print(addr, name, status, ""); +} + +/* + * dbg_queue: prints a QUEUE event + * @addr:   endpoint address + * @req:    USB request + * @status: status + */ +void dbg_queue(u8 addr, const struct usb_request *req, int status) +{ +	char msg[DBG_DATA_MSG]; + +	if (req != NULL) { +		scnprintf(msg, sizeof(msg), +			  "%d %d", !req->no_interrupt, req->length); +		dbg_print(addr, "QUEUE", status, msg); +	} +} + +/** + * dbg_setup: prints a SETUP event + * @addr: endpoint address + * @req:  setup request + */ +void dbg_setup(u8 addr, const struct usb_ctrlrequest *req) +{ +	char msg[DBG_DATA_MSG]; + +	if (req != NULL) { +		scnprintf(msg, sizeof(msg), +			  "%02X %02X %04X %04X %d", req->bRequestType, +			  req->bRequest, le16_to_cpu(req->wValue), +			  le16_to_cpu(req->wIndex), le16_to_cpu(req->wLength)); +		dbg_print(addr, "SETUP", 0, msg); +	} +} + +/** + * show_events: displays the event buffer + * + * Check "device.h" for details + */ +static ssize_t show_events(struct device *dev, struct device_attribute *attr, +			   char *buf) +{ +	unsigned long flags; +	unsigned i, j, n = 0; + +	if (attr == NULL || buf == NULL) { +		dev_err(dev->parent, "[%s] EINVAL\n", __func__); +		return 0; +	} + +	read_lock_irqsave(&dbg_data.lck, flags); + +	i = dbg_data.idx; +	for (dbg_dec(&i); i != dbg_data.idx; dbg_dec(&i)) { +		n += strlen(dbg_data.buf[i]); +		if (n >= PAGE_SIZE) { +			n -= strlen(dbg_data.buf[i]); +			break; +		} +	} +	for (j = 0, dbg_inc(&i); j < n; dbg_inc(&i)) +		j += scnprintf(buf + j, PAGE_SIZE - j, +			       "%s", dbg_data.buf[i]); + +	read_unlock_irqrestore(&dbg_data.lck, flags); + +	return n; +} + +/** + * store_events: configure if events are going to be also printed to console + * + * Check "device.h" for details + */ +static ssize_t store_events(struct device *dev, struct device_attribute *attr, +			    const char *buf, size_t count) +{ +	unsigned tty; + +	if (attr == NULL || buf == NULL) { +		dev_err(dev, "[%s] EINVAL\n", __func__); +		goto done; +	} + +	if (sscanf(buf, "%u", &tty) != 1 || tty > 1) { +		dev_err(dev, "<1|0>: enable|disable console log\n"); +		goto done; +	} + +	dbg_data.tty = tty; +	dev_info(dev, "tty = %u", dbg_data.tty); + + done: +	return count; +} +static DEVICE_ATTR(events, S_IRUSR | S_IWUSR, show_events, store_events); + +/** + * show_inters: interrupt status, enable status and historic + * + * Check "device.h" for details + */ +static ssize_t show_inters(struct device *dev, struct device_attribute *attr, +			   char *buf) +{ +	struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); +	unsigned long flags; +	u32 intr; +	unsigned i, j, n = 0; + +	if (attr == NULL || buf == NULL) { +		dev_err(udc->dev, "[%s] EINVAL\n", __func__); +		return 0; +	} + +	spin_lock_irqsave(&udc->lock, flags); + +	/*n += scnprintf(buf + n, PAGE_SIZE - n, +		       "status = %08x\n", hw_read_intr_status(udc)); +	n += scnprintf(buf + n, PAGE_SIZE - n, +	"enable = %08x\n", hw_read_intr_enable(udc));*/ + +	n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n", +		       isr_statistics.test); +	n += scnprintf(buf + n, PAGE_SIZE - n, "? ui  = %d\n", +		       isr_statistics.ui); +	n += scnprintf(buf + n, PAGE_SIZE - n, "? uei = %d\n", +		       isr_statistics.uei); +	n += scnprintf(buf + n, PAGE_SIZE - n, "? pci = %d\n", +		       isr_statistics.pci); +	n += scnprintf(buf + n, PAGE_SIZE - n, "? uri = %d\n", +		       isr_statistics.uri); +	n += scnprintf(buf + n, PAGE_SIZE - n, "? sli = %d\n", +		       isr_statistics.sli); +	n += scnprintf(buf + n, PAGE_SIZE - n, "*none = %d\n", +		       isr_statistics.none); +	n += scnprintf(buf + n, PAGE_SIZE - n, "*hndl = %d\n", +		       isr_statistics.hndl.cnt); + +	for (i = isr_statistics.hndl.idx, j = 0; j <= ISR_MASK; j++, i++) { +		i   &= ISR_MASK; +		intr = isr_statistics.hndl.buf[i]; + +		if (USBi_UI  & intr) +			n += scnprintf(buf + n, PAGE_SIZE - n, "ui  "); +		intr &= ~USBi_UI; +		if (USBi_UEI & intr) +			n += scnprintf(buf + n, PAGE_SIZE - n, "uei "); +		intr &= ~USBi_UEI; +		if (USBi_PCI & intr) +			n += scnprintf(buf + n, PAGE_SIZE - n, "pci "); +		intr &= ~USBi_PCI; +		if (USBi_URI & intr) +			n += scnprintf(buf + n, PAGE_SIZE - n, "uri "); +		intr &= ~USBi_URI; +		if (USBi_SLI & intr) +			n += scnprintf(buf + n, PAGE_SIZE - n, "sli "); +		intr &= ~USBi_SLI; +		if (intr) +			n += scnprintf(buf + n, PAGE_SIZE - n, "??? "); +		if (isr_statistics.hndl.buf[i]) +			n += scnprintf(buf + n, PAGE_SIZE - n, "\n"); +	} + +	spin_unlock_irqrestore(&udc->lock, flags); + +	return n; +} + +/** + * store_inters: enable & force or disable an individual interrutps + *                   (to be used for test purposes only) + * + * Check "device.h" for details + */ +static ssize_t store_inters(struct device *dev, struct device_attribute *attr, +			    const char *buf, size_t count) +{ +	struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); +	unsigned long flags; +	unsigned en, bit; + +	if (attr == NULL || buf == NULL) { +		dev_err(udc->dev, "EINVAL\n"); +		goto done; +	} + +	if (sscanf(buf, "%u %u", &en, &bit) != 2 || en > 1) { +		dev_err(udc->dev, "<1|0> <bit>: enable|disable interrupt\n"); +		goto done; +	} + +	spin_lock_irqsave(&udc->lock, flags); +	if (en) { +		if (hw_intr_force(udc, bit)) +			dev_err(dev, "invalid bit number\n"); +		else +			isr_statistics.test++; +	} else { +		if (hw_intr_clear(udc, bit)) +			dev_err(dev, "invalid bit number\n"); +	} +	spin_unlock_irqrestore(&udc->lock, flags); + + done: +	return count; +} +static DEVICE_ATTR(inters, S_IRUSR | S_IWUSR, show_inters, store_inters); + +/** + * show_port_test: reads port test mode + * + * Check "device.h" for details + */ +static ssize_t show_port_test(struct device *dev, +			      struct device_attribute *attr, char *buf) +{ +	struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); +	unsigned long flags; +	unsigned mode; + +	if (attr == NULL || buf == NULL) { +		dev_err(udc->dev, "EINVAL\n"); +		return 0; +	} + +	spin_lock_irqsave(&udc->lock, flags); +	mode = hw_port_test_get(udc); +	spin_unlock_irqrestore(&udc->lock, flags); + +	return scnprintf(buf, PAGE_SIZE, "mode = %u\n", mode); +} + +/** + * store_port_test: writes port test mode + * + * Check "device.h" for details + */ +static ssize_t store_port_test(struct device *dev, +			       struct device_attribute *attr, +			       const char *buf, size_t count) +{ +	struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); +	unsigned long flags; +	unsigned mode; + +	if (attr == NULL || buf == NULL) { +		dev_err(udc->dev, "[%s] EINVAL\n", __func__); +		goto done; +	} + +	if (sscanf(buf, "%u", &mode) != 1) { +		dev_err(udc->dev, "<mode>: set port test mode"); +		goto done; +	} + +	spin_lock_irqsave(&udc->lock, flags); +	if (hw_port_test_set(udc, mode)) +		dev_err(udc->dev, "invalid mode\n"); +	spin_unlock_irqrestore(&udc->lock, flags); + + done: +	return count; +} +static DEVICE_ATTR(port_test, S_IRUSR | S_IWUSR, +		   show_port_test, store_port_test); + +/** + * show_qheads: DMA contents of all queue heads + * + * Check "device.h" for details + */ +static ssize_t show_qheads(struct device *dev, struct device_attribute *attr, +			   char *buf) +{ +	struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); +	unsigned long flags; +	unsigned i, j, n = 0; + +	if (attr == NULL || buf == NULL) { +		dev_err(udc->dev, "[%s] EINVAL\n", __func__); +		return 0; +	} + +	spin_lock_irqsave(&udc->lock, flags); +	for (i = 0; i < udc->hw_ep_max/2; i++) { +		struct ci13xxx_ep *mEpRx = &udc->ci13xxx_ep[i]; +		struct ci13xxx_ep *mEpTx = +			&udc->ci13xxx_ep[i + udc->hw_ep_max/2]; +		n += scnprintf(buf + n, PAGE_SIZE - n, +			       "EP=%02i: RX=%08X TX=%08X\n", +			       i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma); +		for (j = 0; j < (sizeof(struct ci13xxx_qh)/sizeof(u32)); j++) { +			n += scnprintf(buf + n, PAGE_SIZE - n, +				       " %04X:    %08X    %08X\n", j, +				       *((u32 *)mEpRx->qh.ptr + j), +				       *((u32 *)mEpTx->qh.ptr + j)); +		} +	} +	spin_unlock_irqrestore(&udc->lock, flags); + +	return n; +} +static DEVICE_ATTR(qheads, S_IRUSR, show_qheads, NULL); + +/** + * show_registers: dumps all registers + * + * Check "device.h" for details + */ +#define DUMP_ENTRIES	512 +static ssize_t show_registers(struct device *dev, +			      struct device_attribute *attr, char *buf) +{ +	struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); +	unsigned long flags; +	u32 *dump; +	unsigned i, k, n = 0; + +	if (attr == NULL || buf == NULL) { +		dev_err(udc->dev, "[%s] EINVAL\n", __func__); +		return 0; +	} + +	dump = kmalloc(sizeof(u32) * DUMP_ENTRIES, GFP_KERNEL); +	if (!dump) { +		dev_err(udc->dev, "%s: out of memory\n", __func__); +		return 0; +	} + +	spin_lock_irqsave(&udc->lock, flags); +	k = hw_register_read(udc, dump, DUMP_ENTRIES); +	spin_unlock_irqrestore(&udc->lock, flags); + +	for (i = 0; i < k; i++) { +		n += scnprintf(buf + n, PAGE_SIZE - n, +			       "reg[0x%04X] = 0x%08X\n", +			       i * (unsigned)sizeof(u32), dump[i]); +	} +	kfree(dump); + +	return n; +} + +/** + * store_registers: writes value to register address + * + * Check "device.h" for details + */ +static ssize_t store_registers(struct device *dev, +			       struct device_attribute *attr, +			       const char *buf, size_t count) +{ +	struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); +	unsigned long addr, data, flags; + +	if (attr == NULL || buf == NULL) { +		dev_err(udc->dev, "[%s] EINVAL\n", __func__); +		goto done; +	} + +	if (sscanf(buf, "%li %li", &addr, &data) != 2) { +		dev_err(udc->dev, +			"<addr> <data>: write data to register address\n"); +		goto done; +	} + +	spin_lock_irqsave(&udc->lock, flags); +	if (hw_register_write(udc, addr, data)) +		dev_err(udc->dev, "invalid address range\n"); +	spin_unlock_irqrestore(&udc->lock, flags); + + done: +	return count; +} +static DEVICE_ATTR(registers, S_IRUSR | S_IWUSR, +		   show_registers, store_registers); + +/** + * show_requests: DMA contents of all requests currently queued (all endpts) + * + * Check "device.h" for details + */ +static ssize_t show_requests(struct device *dev, struct device_attribute *attr, +			     char *buf) +{ +	struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); +	unsigned long flags; +	struct list_head   *ptr = NULL; +	struct ci13xxx_req *req = NULL; +	unsigned i, j, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32); + +	if (attr == NULL || buf == NULL) { +		dev_err(udc->dev, "[%s] EINVAL\n", __func__); +		return 0; +	} + +	spin_lock_irqsave(&udc->lock, flags); +	for (i = 0; i < udc->hw_ep_max; i++) +		list_for_each(ptr, &udc->ci13xxx_ep[i].qh.queue) +		{ +			req = list_entry(ptr, struct ci13xxx_req, queue); + +			n += scnprintf(buf + n, PAGE_SIZE - n, +					"EP=%02i: TD=%08X %s\n", +					i % udc->hw_ep_max/2, (u32)req->dma, +					((i < udc->hw_ep_max/2) ? "RX" : "TX")); + +			for (j = 0; j < qSize; j++) +				n += scnprintf(buf + n, PAGE_SIZE - n, +						" %04X:    %08X\n", j, +						*((u32 *)req->ptr + j)); +		} +	spin_unlock_irqrestore(&udc->lock, flags); + +	return n; +} +static DEVICE_ATTR(requests, S_IRUSR, show_requests, NULL); + +/** + * dbg_create_files: initializes the attribute interface + * @dev: device + * + * This function returns an error code + */ +int dbg_create_files(struct device *dev) +{ +	int retval = 0; + +	if (dev == NULL) +		return -EINVAL; +	retval = device_create_file(dev, &dev_attr_device); +	if (retval) +		goto done; +	retval = device_create_file(dev, &dev_attr_driver); +	if (retval) +		goto rm_device; +	retval = device_create_file(dev, &dev_attr_events); +	if (retval) +		goto rm_driver; +	retval = device_create_file(dev, &dev_attr_inters); +	if (retval) +		goto rm_events; +	retval = device_create_file(dev, &dev_attr_port_test); +	if (retval) +		goto rm_inters; +	retval = device_create_file(dev, &dev_attr_qheads); +	if (retval) +		goto rm_port_test; +	retval = device_create_file(dev, &dev_attr_registers); +	if (retval) +		goto rm_qheads; +	retval = device_create_file(dev, &dev_attr_requests); +	if (retval) +		goto rm_registers; +	return 0; + + rm_registers: +	device_remove_file(dev, &dev_attr_registers); + rm_qheads: +	device_remove_file(dev, &dev_attr_qheads); + rm_port_test: +	device_remove_file(dev, &dev_attr_port_test); + rm_inters: +	device_remove_file(dev, &dev_attr_inters); + rm_events: +	device_remove_file(dev, &dev_attr_events); + rm_driver: +	device_remove_file(dev, &dev_attr_driver); + rm_device: +	device_remove_file(dev, &dev_attr_device); + done: +	return retval; +} + +/** + * dbg_remove_files: destroys the attribute interface + * @dev: device + * + * This function returns an error code + */ +int dbg_remove_files(struct device *dev) +{ +	if (dev == NULL) +		return -EINVAL; +	device_remove_file(dev, &dev_attr_requests); +	device_remove_file(dev, &dev_attr_registers); +	device_remove_file(dev, &dev_attr_qheads); +	device_remove_file(dev, &dev_attr_port_test); +	device_remove_file(dev, &dev_attr_inters); +	device_remove_file(dev, &dev_attr_events); +	device_remove_file(dev, &dev_attr_driver); +	device_remove_file(dev, &dev_attr_device); +	return 0; +}  |