From d717b83056e5076a64c9471dd85573de409582d7 Mon Sep 17 00:00:00 2001 From: tzafrir Date: Thu, 25 Jan 2007 10:48:33 +0000 Subject: * Xbus protocol version: 2.4 (Zaptel 1.2.12/1.4.0 had 2.3). XPS Init scripts renamed accordingly. * Performance improvements for multi-XPD (span) devices. * Astribank BRI driver (in next commit). * Changes under /proc: - XBUS and XPD numbers have two digits. - Every script wildcard should be replaced from XBUS-? to XBUS-[0-9]* - Added /proc/xpp/XBUS-*/XPD-*/blink: echo 1 to start and 0 to stop. * Several countries (South Africa, UAE, anybody else) require a shorter ring delay. Adjust FXO reg 0x17 (23)'s bits 0:2 to 011. * Use tasklets to move most of the interrupt PCM copying out of the interrupt. * Debugfs-based code to dump data to userspace (used to debug BRI D channel). * Pretend every 2.6.9 actually has later RHEL's typedefs. * fpga_load supports /dev/bus/usb . * Fixed physical order sorting in genzaptelconf. git-svn-id: http://svn.digium.com/svn/zaptel/branches/1.2@1966 5390a7c7-147a-4af0-8ec9-7488f05a26cb --- xpp/xbus-core.c | 421 +++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 342 insertions(+), 79 deletions(-) (limited to 'xpp/xbus-core.c') diff --git a/xpp/xbus-core.c b/xpp/xbus-core.c index 59db079..d39c10c 100644 --- a/xpp/xbus-core.c +++ b/xpp/xbus-core.c @@ -37,6 +37,9 @@ #include "xpd.h" #include "xpp_zap.h" #include "xbus-core.h" +#ifdef XPP_DEBUGFS +#include "xpp_log.h" +#endif #include "zap_debug.h" static const char rcsid[] = "$Id$"; @@ -84,53 +87,270 @@ static struct proc_dir_entry *proc_xbuses = NULL; static DEVICE_ATTR(connector, S_IRUGO, connector_show, NULL); static DEVICE_ATTR(status, S_IRUGO, status_show, NULL); -/*------------------------- Packet Handling ------------------------*/ -static kmem_cache_t *packet_cache = NULL; -static atomic_t xpacket_count = ATOMIC_INIT(0); - -/** - * Allocates a new XPP packet. - * @xbus The XPP bus in which the packet will flow (for counters - * maintenance) - * @flags Flags for kernel memory allocation. - * @returns A pointer to the new packet, or NULL in case of failure. - * - * - * Packet allocation/deallocation: - * Sent packets: - * - Allocated by protocol commands - * - Deallocated by xmus_xmitter - * Receive packets: - * - Allocated/deallocated by xbus_xmiter +/*------------------------- Debugfs Handling -----------------------*/ +#ifdef XPP_DEBUGFS + +#define DEBUGFS_BUFSIZ 4096 /* must be power of two, otherwise POS_IN_BUF will have to use '%' instead of '&' */ +#define POS_IN_BUF(x) ((x) & (DEBUGFS_BUFSIZ-1)) + +struct debugfs_data { + spinlock_t lock; + xbus_t *xbus; + char buffer[DEBUGFS_BUFSIZ]; + unsigned long head, tail; /* reading and writing are performed at position (head % BUF_SIZ) and (tail % BUF_SIZ) */ + wait_queue_head_t queue; +}; + +static unsigned long add_to_buf(struct debugfs_data *d, unsigned long tail, const void *buf, unsigned long len) +{ + unsigned long count = min(len, (unsigned long)(DEBUGFS_BUFSIZ - POS_IN_BUF(tail))); + memcpy(d->buffer + POS_IN_BUF(tail), buf, count); /* fill starting at position tail */ + memcpy(d->buffer, (u_char *)buf + count, len - count); /* fill leftover */ + return len; +} + +int xbus_log(xbus_t *xbus, xpd_t *xpd, int direction, const void *buf, unsigned long len) +{ + unsigned long tail; + unsigned long flags; + struct debugfs_data *d; + struct log_header header; + int ret = 0; + + BUG_ON(!xbus); + BUG_ON(!xpd); + BUG_ON(sizeof(struct log_header) + len > DEBUGFS_BUFSIZ); + d = xbus->debugfs_data; + if (!d) /* no consumer process */ + return ret; + spin_lock_irqsave(&d->lock, flags); + if (sizeof(struct log_header) + len > DEBUGFS_BUFSIZ - (d->tail - d->head)) { + ret = -ENOSPC; + DBG("Dropping debugfs data of len %lu, free space is %lu\n", sizeof(struct log_header) + len, + DEBUGFS_BUFSIZ - (d->tail - d->head)); + goto out; + } + header.len = sizeof(struct log_header) + len; + header.time = jiffies_to_msecs(jiffies); + header.xpd_num = xpd->id; + header.direction = (char)direction; + tail = d->tail; + tail += add_to_buf(d, tail, &header, sizeof(header)); + tail += add_to_buf(d, tail, buf, len); + d->tail = tail; + wake_up_interruptible(&d->queue); +out: + spin_unlock_irqrestore(&d->lock, flags); + return ret; +} + +static struct dentry *debugfs_root = NULL; +static int debugfs_open(struct inode *inode, struct file *file); +static ssize_t debugfs_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos); +static int debugfs_release(struct inode *inode, struct file *file); + +struct file_operations debugfs_operations = { + .open = debugfs_open, + .read = debugfs_read, + .release = debugfs_release, +}; + +/* + * As part of the "inode diet" the private data member of struct inode + * has changed in 2.6.19. However, Fedore Core 6 adopted this change + * a bit earlier (2.6.18). If you use vanila kernel (or Debian Etch) + * Change the following test from 2,6,18 to 2,6,19. */ -xpacket_t *xbus_packet_new(xbus_t *xbus, gfp_t flags) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) +#define I_PRIVATE(inode) ((inode)->u.generic_ip) +#else +#define I_PRIVATE(inode) ((inode)->i_private) +#endif + +static int debugfs_open(struct inode *inode, struct file *file) { - xpacket_t *pack; + xbus_t *xbus = I_PRIVATE(inode); + struct debugfs_data *d; + struct log_global_header gheader; - /* To avoid races we increament counter in advance and decrement it later - * in case of failure */ - atomic_inc(&xbus->packet_counter); - //DBG("Incremented packet_counter of bus %s (new packet) to %d\n", - // xbus->busname, atomic_read(&xbus->packet_counter)); - pack = kmem_cache_alloc(packet_cache, flags); - if (pack) { - memset(pack, 0, sizeof(xpacket_t)); - atomic_inc(&xpacket_count); - } else { - atomic_dec(&xbus->packet_counter); - //DBG("Decremented packet_counter of bus %s (failed new packet) to %d\n", - // xbus->busname, atomic_read(&xbus->packet_counter)); + BUG_ON(!xbus); + DBG("%s\n", xbus->busname); + if (xbus->debugfs_data) + return -EBUSY; + d = kmalloc(sizeof(struct debugfs_data), GFP_KERNEL); + if (!d) + return -ENOMEM; + try_module_get(THIS_MODULE); + memset(d, 0, sizeof(struct debugfs_data)); + spin_lock_init(&d->lock); + d->xbus = xbus; + d->head = d->tail = 0; + init_waitqueue_head(&d->queue); + file->private_data = d; + + gheader.magic = XPP_LOG_MAGIC; + gheader.version = 1; + d->tail += add_to_buf(d, d->tail, &gheader, sizeof(gheader)); + + xbus->debugfs_data = d; + return 0; +} + +static ssize_t debugfs_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) +{ + struct debugfs_data *d = file->private_data; + size_t len; + + BUG_ON(!d); + BUG_ON(!d->xbus); + DBG("%s\n", d->xbus->busname); + while (d->head == d->tail) { + if (wait_event_interruptible(d->queue, d->head != d->tail)) + return -EAGAIN; + } + len = min(nbytes, (size_t)(d->tail - d->head)); + if (copy_to_user(buf, d->buffer + POS_IN_BUF(d->head), len)) + return -EFAULT; + d->head += len; + /* optimization to avoid future buffer wraparound */ + if (d->head == d->tail) { + unsigned long flags; + spin_lock_irqsave(&d->lock, flags); + if (d->head == d->tail) + d->head = d->tail = 0; + spin_unlock_irqrestore(&d->lock, flags); } - return pack; + return len; } -void xbus_packet_free(xbus_t *xbus, xpacket_t *p) +static int debugfs_release(struct inode *inode, struct file *file) { - kmem_cache_free(packet_cache, p); - atomic_dec(&xpacket_count); - atomic_dec(&xbus->packet_counter); - //DBG("Decremented packet_counter of bus %s (freed packet) to %d\n", - // xbus->busname, atomic_read(&xbus->packet_counter)); + struct debugfs_data *d = file->private_data; + + BUG_ON(!d); + BUG_ON(!d->xbus); + DBG("%s\n", d->xbus->busname); + d->xbus->debugfs_data = NULL; + kfree(d); + module_put(THIS_MODULE); + return 0; +} +#endif + +/*------------------------- Frame Handling ------------------------*/ + +static kmem_cache_t *xframes_cache = NULL; +xframe_t *xbus_xframe_new(xbus_t *xbus, gfp_t flags) +{ + xframe_t *frm; + + frm = kmem_cache_alloc(xframes_cache, flags); + if(!frm) + return NULL; + memset(frm, 0, sizeof(xframe_t) + XFRAME_DATASIZE); + atomic_set(&frm->frame_len, 0); + frm->packets = (byte *)frm + sizeof(xframe_t); + return frm; +} + +void xbus_xframe_free(xbus_t *xbus, xframe_t *p) +{ + kmem_cache_free(xframes_cache, p); +} + +/* + * Return pointer to next packet slot in the frame + * or NULL if the frame is full. + */ +xpacket_t *xframe_next_packet(xframe_t *frm, int len) +{ + int newlen = atomic_add_return(len, &frm->frame_len); +// DBG("len=%d, newlen=%d, frm->frame_len=%d\n", len, newlen, XFRAME_LEN(frm)); + if (newlen > XFRAME_DATASIZE) { + atomic_sub(len, &frm->frame_len); + return NULL; + } + return (xpacket_t *)(frm->packets + newlen - len); +} + +static spinlock_t serialize_dump_xframe = SPIN_LOCK_UNLOCKED; + +static void do_hexdump(const char msg[], byte *data, uint16_t len) +{ + int i; + + for(i = 0; i < len; i++) + DBG("%s: %3d> %02X\n", msg, i, data[i]); +} + +void dump_xframe(const char msg[], const xbus_t *xbus, const xframe_t *xframe) +{ + const uint16_t frame_len = XFRAME_LEN(xframe); + xpacket_t *pack; + uint16_t pos = 0; + uint16_t nextpos; + int num = 1; + bool do_print; + static int rate_limit; + unsigned long flags; + + spin_lock_irqsave(&serialize_dump_xframe, flags); + do { + if(pos >= XFRAME_DATASIZE) { + if(printk_ratelimit()) { + ERR("%s: xframe overflow (%d bytes)\n", + msg, frame_len); + do_hexdump(msg, xframe->packets, frame_len); + } + break; + } + if(pos > frame_len) { + if(printk_ratelimit()) { + ERR("%s: packet overflow pos=%d frame_len=%d\n", + msg, pos, frame_len); + do_hexdump(msg, xframe->packets, frame_len); + } + break; + } + pack = (xpacket_t *)&xframe->packets[pos]; + if(pack->datalen <= 0) { + if(printk_ratelimit()) { + ERR("%s: xframe -- bad datalen=%d pos=%d frame_len=%d\n", + msg, pack->datalen, pos, frame_len); + do_hexdump(msg, xframe->packets, frame_len); + } + break; + } + nextpos = pos + pack->datalen; + if(nextpos > frame_len) { + if(printk_ratelimit()) { + ERR("%s: packet overflow nextpos=%d frame_len=%d\n", + msg, nextpos, frame_len); + do_hexdump(msg, xframe->packets, frame_len); + } + break; + } + do_print = 0; + if(pack->opcode != XPROTO_NAME(GLOBAL,PCM_READ) && + pack->opcode != XPROTO_NAME(GLOBAL,PCM_WRITE)) + do_print = 1; + if((print_dbg & DBG_PCM) && ((rate_limit % 1003) == 0)) + do_print = 1; + if(do_print) { + if(num == 1) + DBG("%s: %s: frame_len=%d.\n", + msg, xbus->busname, frame_len); + DBG(" %3d. DATALEN=%d OP=0x%02X XPD-%d-%d (pos=%d)\n", + num, pack->datalen, pack->opcode, + pack->addr.unit, pack->addr.subunit, pos); + dump_packet(" ", pack, print_dbg); + } + num++; + pos = nextpos; + if(pos >= frame_len) + break; + } while(1); + spin_unlock_irqrestore(&serialize_dump_xframe, flags); } @@ -142,7 +362,7 @@ xbus_t *xbus_of(int xbus_num) return xbuses_array[xbus_num]; } -xpd_t *xpd_of(xbus_t *xbus, int xpd_num) +xpd_t *xpd_of(const xbus_t *xbus, int xpd_num) { if(!VALID_XPD_NUM(xpd_num)) return NULL; @@ -220,7 +440,6 @@ static int xbus_poll(void *data) struct list_head additions_list; int count_removed; int count_added; - int xpd_num; xbus_t *xbus = data; if(!down_read_trylock(&xbus->in_use)) { @@ -277,8 +496,7 @@ static int xbus_poll(void *data) xpd_t *xpd; BUG_ON(card_desc->magic != CARD_DESC_MAGIC); - xpd_num = xpd_addr2num(&card_desc->xpd_addr); - xpd = xpd_of(xbus, xpd_num); + xpd = xpd_by_addr(xbus, card_desc->xpd_addr.unit, card_desc->xpd_addr.subunit); if(xpd && type == XPD_TYPE_NOMODULE) { /* card removal */ list_move_tail(card, &removal_list); @@ -306,8 +524,7 @@ static int xbus_poll(void *data) xpd_t *xpd; list_del(card); - xpd_num = xpd_addr2num(&card_desc->xpd_addr); - xpd = xpd_of(xbus, xpd_num); + xpd = xpd_by_addr(xbus, card_desc->xpd_addr.unit, card_desc->xpd_addr.subunit); if(xpd) xpd_disconnect(xpd); kfree(card); @@ -338,8 +555,8 @@ void xbus_activate(xbus_t *xbus) ops = xbus->ops; BUG_ON(!ops); /* Sanity checks */ - BUG_ON(!ops->packet_send); - BUG_ON(!ops->packet_new || !ops->packet_free); + BUG_ON(!ops->xframe_send); + BUG_ON(!ops->xframe_new || !ops->xframe_free); xbus->hardware_exists = 1; DBG("Activating: %s\n", xbus->busname); /* Poll it */ @@ -415,6 +632,16 @@ static void xbus_free(xbus_t *xbus) xbuses_array[xbus->num] = NULL; bus_count--; spin_unlock_irqrestore(&xbuses_lock, flags); +#ifdef XPP_DEBUGFS + if(xbus->debugfs_dir) { + if(xbus->debugfs_file) { + DBG("Removing debugfs file for %s\n", xbus->busname); + debugfs_remove(xbus->debugfs_file); + } + DBG("Removing debugfs directory for %s\n", xbus->busname); + debugfs_remove(xbus->debugfs_dir); + } +#endif #ifdef CONFIG_PROC_FS if(xbus->proc_xbus_dir) { if(xbus->proc_xbus_summary) { @@ -467,7 +694,7 @@ xbus_t *xbus_new(xbus_ops_t *ops) /* Init data structures */ spin_lock_init(&xbus->lock); - snprintf(xbus->busname, XBUS_NAMELEN, "XBUS-%d", xbus->num); + snprintf(xbus->busname, XBUS_NAMELEN, "XBUS-%02d", xbus->num); INFO("New xbus: %s\n", xbus->busname); init_waitqueue_head(&xbus->packet_cache_empty); atomic_set(&xbus->packet_counter, 0); @@ -540,16 +767,28 @@ xbus_t *xbus_new(xbus_ops_t *ops) xbus->proc_xbus_command->data = xbus; xbus->proc_xbus_command->owner = THIS_MODULE; #endif +#endif +#ifdef XPP_DEBUGFS + xbus->debugfs_dir = debugfs_create_dir(xbus->busname, debugfs_root); + if(!xbus->debugfs_dir) { + ERR("Failed to create debugfs directory for %s\n", xbus->busname); + goto nobus; + } + xbus->debugfs_file = debugfs_create_file("dchannel", S_IFREG|S_IRUGO|S_IWUSR, xbus->debugfs_dir, xbus, &debugfs_operations); + if(!xbus->debugfs_file) { + ERR("Failed to create dchannel file for %s\n", xbus->busname); + goto nobus; + } #endif /* Sanity checks */ - if(!ops->packet_send) { - ERR("%s: missing mandatory handler: packet_send\n", __FUNCTION__); + if(!ops->xframe_send) { + ERR("%s: missing mandatory handler: xframe_send\n", __FUNCTION__); goto nobus; } - if(!ops->packet_new || !ops->packet_free) { + if(!ops->xframe_new || !ops->xframe_free) { NOTICE("%s: Using default packet allocators\n", __FUNCTION__); - ops->packet_new = xbus_packet_new; - ops->packet_free = xbus_packet_free; + ops->xframe_new = xbus_xframe_new; + ops->xframe_free = xbus_xframe_free; } xbus->ops = ops; @@ -685,7 +924,8 @@ static int xbus_read_waitfor_xpds(char *page, char **start, off_t off, int count } DBG("%s: Finished initialization of %d XPD's in %d seconds.\n", xbus->busname, MAX_XPDS, (INITIALIZATION_TIMEOUT - ret)/HZ); spin_lock_irqsave(&xbus->lock, flags); - len += sprintf(page + len, "XPDS_READY: %d/%d\n", + len += sprintf(page + len, "XPDS_READY: %s: %d/%d\n", + xbus->busname, atomic_read(&xbus->count_xpds_initialized), atomic_read(&xbus->count_xpds_to_initialize)); spin_unlock_irqrestore(&xbus->lock, flags); @@ -709,8 +949,9 @@ static int proc_xbus_command_write(struct file *file, const char __user *buffer, xbus_t *xbus = data; xpacket_t *pack; char *p; - byte *pack_contents; + byte *pack_start; byte *q; + xframe_t *xframe; if(count >= MAX_PROC_WRITE) { ERR("%s: line too long\n", __FUNCTION__); @@ -719,10 +960,7 @@ static int proc_xbus_command_write(struct file *file, const char __user *buffer, if(copy_from_user(buf, buffer, count)) return -EINVAL; buf[count] = '\0'; - pack = xbus->ops->packet_new(xbus, GFP_KERNEL); - if(!pack) - return -ENOMEM; - q = pack_contents = (byte *)&pack->content; + q = pack_start = buf; for(p = buf; *p;) { int val; char hexdigit[3]; @@ -734,7 +972,7 @@ static int proc_xbus_command_write(struct file *file, const char __user *buffer, if(!isxdigit(*p)) { ERR("%s: %s: bad hex value ASCII='0x%X' at position %d\n", __FUNCTION__, xbus->busname, *p, p - buf); - goto err; + return -EINVAL; } hexdigit[0] = *p++; hexdigit[1] = '\0'; @@ -744,18 +982,22 @@ static int proc_xbus_command_write(struct file *file, const char __user *buffer, if(sscanf(hexdigit, "%2X", &val) != 1) { ERR("%s: %s: bad hex value '%s' at position %d\n", __FUNCTION__, xbus->busname, hexdigit, p - buf); - goto err; + return -EINVAL; } *q++ = val; // DBG("%s: %s: '%s' val=%d\n", __FUNCTION__, xbus->busname, hexdigit, val); } - pack->datalen = q - pack_contents - - sizeof(pack->content.opcode) - sizeof(pack->content.addr); - packet_send(xbus, pack); + xframe = xbus->ops->xframe_new(xbus, GFP_KERNEL); + if(!xframe) + return -ENOMEM; + pack = xframe_next_packet(xframe, q - pack_start); + if(!pack) { + xbus->ops->xframe_free(xbus, xframe); + return -ENOMEM; + } + memcpy(pack, pack_start, q - pack_start); /* FRAMES: checksum? */ + xframe_send(xbus, xframe); return count; -err: - xbus->ops->packet_free(xbus, pack); - return -EINVAL; } #endif @@ -852,6 +1094,12 @@ static void xbus_core_cleanup(void) destroy_workqueue(xpp_worker); xpp_worker = NULL; } +#ifdef XPP_DEBUGFS + if(debugfs_root) { + DBG("Removing xpp from debugfs\n"); + debugfs_remove(debugfs_root); + } +#endif #ifdef CONFIG_PROC_FS if(proc_xbuses) { DBG("Removing " PROC_XBUSES " from proc\n"); @@ -859,8 +1107,8 @@ static void xbus_core_cleanup(void) proc_xbuses = NULL; } #endif - if(packet_cache) - kmem_cache_destroy(packet_cache); + if(xframes_cache) + kmem_cache_destroy(xframes_cache); } int __init xbus_core_init(void) @@ -870,35 +1118,45 @@ int __init xbus_core_init(void) #ifdef PROTOCOL_DEBUG INFO("FEATURE: %s with PROTOCOL_DEBUG\n", THIS_MODULE->name); #endif - packet_cache = kmem_cache_create("xpp_packets", - sizeof(xpacket_t), + xframes_cache = kmem_cache_create("xpp_frames", + sizeof(xframe_t) + XFRAME_DATASIZE, 0, 0, NULL, NULL); - if(!packet_cache) { + if(!xframes_cache) { return -ENOMEM; } xpp_worker = create_singlethread_workqueue("xppworker"); if(!xpp_worker) { ERR("Failed to create card detector workqueue.\n"); - xbus_core_cleanup(); - return -ENOMEM; + ret = -ENOMEM; + goto err; } #ifdef CONFIG_PROC_FS proc_xbuses = create_proc_read_entry(PROC_XBUSES, 0444, xpp_proc_toplevel, read_proc_xbuses, NULL); if (!proc_xbuses) { ERR("Failed to create proc file %s\n", PROC_XBUSES); - xbus_core_cleanup(); - return -EFAULT; + ret = -EFAULT; + goto err; } proc_xbuses->owner = THIS_MODULE; +#endif +#ifdef XPP_DEBUGFS + DBG("Creating debugfs xpp root\n"); + debugfs_root = debugfs_create_dir("xpp", NULL); + if(!debugfs_root) { + ret = -EFAULT; + goto err; + } #endif ret = bus_register(&xbus_bus_type); if(ret) { ERR("%s: bus_register failed. Error number %d", __FUNCTION__, ret); - xbus_core_cleanup(); - return ret; + goto err; } return 0; +err: + xbus_core_cleanup(); + return ret; } @@ -922,3 +1180,8 @@ EXPORT_SYMBOL(xbus_remove); EXPORT_SYMBOL(xbus_activate); EXPORT_SYMBOL(xbus_disconnect); EXPORT_SYMBOL(xbus_reset_counters); +EXPORT_SYMBOL(xframe_next_packet); +EXPORT_SYMBOL(dump_xframe); +#ifdef XPP_DEBUGFS +EXPORT_SYMBOL(xbus_log); +#endif -- cgit v1.2.3