/* * Written by Oron Peled * Copyright (C) 2004-2006, Xorcom * * 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. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) # warning "This module is tested only with 2.6 kernels" #endif #include #include #include #include #ifdef PROTOCOL_DEBUG #include #endif #include #include #include /* for msleep() to debug */ #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$"; /* Defines */ #define POLL_TIMEOUT (2*MAX_XPDS) /* in jiffies */ #define INITIALIZATION_TIMEOUT (60*HZ) /* in jiffies */ #define PROC_XBUSES "xbuses" #define PROC_XBUS_SUMMARY "summary" #define PROC_XBUS_WAITFOR_XPDS "waitfor_xpds" #ifdef PROTOCOL_DEBUG #define PROC_XBUS_COMMAND "command" static int proc_xbus_command_write(struct file *file, const char __user *buffer, unsigned long count, void *data); #endif /* Command line parameters */ extern int print_dbg; DEF_PARM(uint, poll_timeout, POLL_TIMEOUT, 0644, "Timeout (in jiffies) waiting for units to reply"); static int xbus_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data); static int xbus_read_waitfor_xpds(char *page, char **start, off_t off, int count, int *eof, void *data); static int read_proc_xmitter(char *page, char **start, off_t off, int count, int *eof, void *data); /* Data structures */ static spinlock_t xbuses_lock = SPIN_LOCK_UNLOCKED; static xbus_t *xbuses_array[MAX_BUSES] = {}; static int bus_count = 0; static struct proc_dir_entry *proc_xbuses = NULL; /* * Command Statistics */ uint command_count; ulong sum_latency; struct proc_dir_entry *proc_commands; #define PROC_CMDS "cmds" /*------------------------- 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; XPD_DBG(xpd, "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->xbus_idx; 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 such a kernel, Change the * following test from 2,6,19 to 2,6,18. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) #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) { xbus_t *xbus = I_PRIVATE(inode); struct debugfs_data *d; struct log_global_header gheader; BUG_ON(!xbus); XBUS_DBG(xbus, "\n"); 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); XBUS_DBG(d->xbus, "\n"); 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 len; } static int debugfs_release(struct inode *inode, struct file *file) { struct debugfs_data *d = file->private_data; BUG_ON(!d); BUG_ON(!d->xbus); XBUS_DBG(d->xbus, "\n"); d->xbus->debugfs_data = NULL; kfree(d); module_put(THIS_MODULE); return 0; } #endif /*------------------------- Frame Handling ------------------------*/ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) static kmem_cache_t *xframes_cache = NULL; #else static struct kmem_cache *xframes_cache = NULL; #endif 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->xbus = xbus; do_gettimeofday(&frm->tv_created); 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. * * FIXME: we do not use atomic_add_return() because kernel-2.6.8 * does not have it. This make this code a little racy, * but we currently call xframe_next_packet() only in the * PCM loop (xbus_tick() etc.) */ xpacket_t *xframe_next_packet(xframe_t *frm, int len) { int newlen = atomic_read(&frm->frame_len); newlen += len; // DBG("len=%d, newlen=%d, frm->frame_len=%d\n", len, newlen, XFRAME_LEN(frm)); if (newlen > XFRAME_DATASIZE) { return NULL; } atomic_add(len, &frm->frame_len); 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; int print_dbg = 1; 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()) { XBUS_NOTICE(xbus, "%s: xframe overflow (%d bytes)\n", msg, frame_len); do_hexdump(msg, xframe->packets, frame_len); } break; } if(pos > frame_len) { if(printk_ratelimit()) { XBUS_NOTICE(xbus, "%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()) { XBUS_NOTICE(xbus, "%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()) { XBUS_NOTICE(xbus, "%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) XBUS_DBG(xbus, "%s: frame_len=%d.\n", msg, frame_len); XBUS_DBG(xbus, " %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); } /** * * Frame is freed: * - In case of error, by this function. * - Otherwise, by the underlying sending mechanism */ int xframe_send(xbus_t *xbus, xframe_t *xframe) { int ret = -ENODEV; if(!xframe) { DBG("null xframe\n"); return -EINVAL; } if(!xbus) { DBG("null xbus\n"); ret = -EINVAL; goto error; } if (!xbus->hardware_exists) { XBUS_DBG(xbus, "Dropped a xframe -- NO HARDWARE."); ret = -ENODEV; goto error; } if(down_read_trylock(&xbus->in_use)) { ret = xbus->ops->xframe_send(xbus, xframe); XBUS_COUNTER(xbus, TX_BYTES) += XFRAME_LEN(xframe); up_read(&xbus->in_use); } else { XBUS_DBG(xbus, "Dropped xframe. Is in_use\n"); } return ret; error: xbus->ops->xframe_free(xbus, xframe); return ret; } int send_cmd_frame(xbus_t *xbus, xframe_t *xframe) { struct timeval now; struct timeval *created = &xframe->tv_created; ulong usec_diff; command_count++; do_gettimeofday(&now); usec_diff = (now.tv_sec - created->tv_sec) * 1000000 + (now.tv_usec - created->tv_usec); sum_latency += usec_diff; return xframe_send(xbus, xframe); } /*------------------------- Bus Management -------------------------*/ xbus_t *xbus_of(int xbus_num) { if(xbus_num < 0 || xbus_num >= MAX_BUSES) return NULL; return xbuses_array[xbus_num]; } xpd_t *xpd_of(const xbus_t *xbus, int xpd_num) { if(!VALID_XPD_NUM(xpd_num)) return NULL; return xbus->xpds[xpd_num]; } xpd_t *xpd_byaddr(const xbus_t *xbus, uint unit, uint subunit) { if(unit > MAX_UNIT || subunit > MAX_SUBUNIT) return NULL; return xbus->xpds[XPD_IDX(unit,subunit)]; } int xbus_register_xpd(xbus_t *xbus, xpd_t *xpd) { unsigned int xpd_num = xpd->xbus_idx; unsigned long flags; int ret = 0; spin_lock_irqsave(&xbus->lock, flags); if(!VALID_XPD_NUM(xpd_num)) { XBUS_ERR(xbus, "Bad xpd_num = %d\n", xpd_num); ret = -EINVAL; goto out; } if(xbus->xpds[xpd_num] != NULL) { xpd_t *other = xbus->xpds[xpd_num]; XBUS_ERR(xbus, "xpd_num=%d is occupied by %p (%s)\n", xpd_num, other, other->xpdname); ret = -EINVAL; goto out; } xbus->xpds[xpd_num] = xpd; xpd->xbus = xbus; xbus->num_xpds++; out: spin_unlock_irqrestore(&xbus->lock, flags); return ret; } int xbus_unregister_xpd(xbus_t *xbus, xpd_t *xpd) { unsigned int xpd_num = xpd->xbus_idx; unsigned long flags; int ret = -EINVAL; spin_lock_irqsave(&xbus->lock, flags); if(!VALID_XPD_NUM(xpd_num)) { XBUS_ERR(xbus, "%s: Bad xpd_num = %d\n", __FUNCTION__, xpd_num); goto out; } if(xbus->xpds[xpd_num] == NULL) { XBUS_ERR(xbus, "%s: slot xpd_num=%d is empty\n", __FUNCTION__, xpd_num); goto out; } if(xbus->xpds[xpd_num] != xpd) { xpd_t *other = xbus->xpds[xpd_num]; XBUS_ERR(xbus, "%s: slot xpd_num=%d is occupied by %p (%s)\n", __FUNCTION__, xpd_num, other, other->xpdname); goto out; } xbus->xpds[xpd_num] = NULL; xbus->num_xpds--; xpd->xbus = NULL; ret = 0; out: spin_unlock_irqrestore(&xbus->lock, flags); return ret; } /* * This must be called from synchronous (non-interrupt) context * it returns only when all XPD's on the bus are detected and * initialized. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) static void xbus_poll(struct work_struct *work) { struct xbus_poller *poller = container_of(work, struct xbus_poller, xpds_init_work); #else static void xbus_poll(void *data) { struct xbus_poller *poller = data; #endif int id; int ret = 0; unsigned long flags; struct list_head *card; struct list_head *next_card; struct list_head removal_list; struct list_head additions_list; int count_removed; int count_added; xbus_t *xbus; BUG_ON(!poller); xbus = poller->xbus; BUG_ON(!xbus); if(!down_read_trylock(&xbus->in_use)) { XBUS_ERR(xbus, "Is being removed...\n"); return; } msleep(2); /* roundtrip for older polls */ spin_lock_irqsave(&xbus->lock, flags); XBUS_DBG(xbus, "\n"); poller->is_polling = 1; /* * Send out the polls */ for(id = 0; id < MAX_XPDS; id++) { if(!xbus->hardware_exists) break; // XBUS_DBG(xbus, "Polling slot %d\n", id); spin_unlock_irqrestore(&xbus->lock, flags); ret = CALL_PROTO(GLOBAL, DESC_REQ, xbus, NULL, id); spin_lock_irqsave(&xbus->lock, flags); if(ret < 0) { XBUS_ERR(xbus, "Failed sending DESC_REQ to XPD #%d\n", id); goto out; } } spin_unlock_irqrestore(&xbus->lock, flags); /* * Wait for replies */ XBUS_DBG(xbus, "Polled %d XPD's. Waiting for replies max %d jiffies\n", MAX_XPDS, poll_timeout); ret = wait_event_interruptible_timeout(poller->wait_for_polls, atomic_read(&poller->count_poll_answers) >= MAX_XPDS, poll_timeout); if(ret == 0) { XBUS_ERR(xbus, "Poll timeout. Continuing anyway.\n"); /* * Continue processing. Maybe some units did reply. */ } else if(ret < 0) { XBUS_ERR(xbus, "Poll interrupted %d\n", ret); goto out; } else XBUS_DBG(xbus, "Poll finished in %d jiffies.\n", poll_timeout - ret); /* * Build removals/additions lists */ spin_lock_irqsave(&xbus->lock, flags); INIT_LIST_HEAD(&removal_list); INIT_LIST_HEAD(&additions_list); count_removed = 0; count_added = 0; list_for_each_safe(card, next_card, &poller->poll_results) { struct card_desc_struct *card_desc = list_entry(card, struct card_desc_struct, card_list); byte type = card_desc->type; xpd_t *xpd; BUG_ON(card_desc->magic != CARD_DESC_MAGIC); xpd = xpd_byaddr(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); count_removed++; } else if(!xpd && type != XPD_TYPE_NOMODULE) { /* card detection */ list_move_tail(card, &additions_list); count_added++; } else { /* same same */ list_del(card); kfree(card_desc); } } /* * We set this *after* poll is finished, so wait_for_xpd_initialization can * tell we already know how many units we have. */ atomic_set(&poller->count_xpds_to_initialize, count_added); spin_unlock_irqrestore(&xbus->lock, flags); XBUS_INFO(xbus, "Poll results: removals=%d additions=%d\n", count_removed, count_added); /* * Process removals first */ list_for_each_safe(card, next_card, &removal_list) { struct card_desc_struct *card_desc = list_entry(card, struct card_desc_struct, card_list); xpd_t *xpd; list_del(card); xpd = xpd_byaddr(xbus, card_desc->xpd_addr.unit, card_desc->xpd_addr.subunit); if(xpd) xpd_disconnect(xpd); kfree(card); } /* * Now process additions */ list_for_each_safe(card, next_card, &additions_list) { struct card_desc_struct *card_desc = list_entry(card, struct card_desc_struct, card_list); list_del(card); /* FIXME: card_detected() should have a return value for count_xpds_initialized */ card_detected(card_desc); atomic_inc(&poller->count_xpds_initialized); } /* Device-Model */ if((ret = xbus_sysfs_create(xbus)) < 0) { XBUS_ERR(xbus, "%s: xbus_sysfs_create() failed: %d\n", __FUNCTION__, ret); goto out; } wake_up(&poller->wait_for_xpd_initialization); out: poller->is_polling = 0; up_read(&xbus->in_use); return; } void xbus_poller_notify(xbus_t *xbus, struct card_desc_struct *card_desc) { struct xbus_poller *poller; unsigned long flags; BUG_ON(!xbus); poller = xbus->poller; BUG_ON(!poller); if(!poller->is_polling) { XBUS_NOTICE(xbus, "%d%d replied not during poll. Ignore\n", card_desc->xpd_addr.unit, card_desc->xpd_addr.subunit); kfree(card_desc); return; } spin_lock_irqsave(&xbus->lock, flags); if(card_desc->type == XPD_TYPE_NOMODULE) XBUS_COUNTER(xbus, DEV_DESC_EMPTY)++; else XBUS_COUNTER(xbus, DEV_DESC_FULL)++; atomic_inc(&poller->count_poll_answers); list_add_tail(&card_desc->card_list, &poller->poll_results); spin_unlock_irqrestore(&xbus->lock, flags); /* * wake_up only after exiting our critical section. * We suspect that otherwise a spinlock nesting may occur * and cause a panic (if spinlock debugging is compiled in). */ wake_up(&poller->wait_for_polls); return; } static void poller_destroy(struct xbus_poller *poller) { if(!poller) return; if(poller->xbus) { XBUS_DBG(poller->xbus, "detach poller\n"); poller->xbus->poller = NULL; } if (poller->wq) { XBUS_DBG(poller->xbus, "destroy workqueue\n"); flush_workqueue(poller->wq); destroy_workqueue(poller->wq); poller->wq = NULL; } kfree(poller); } /* * Allocate a poller for the xbus including the nessessary workqueue. * May call blocking operations, but only briefly (as we are called * from xbus_new() which is called from khubd. */ static struct xbus_poller *poller_new(xbus_t *xbus) { struct xbus_poller *poller; BUG_ON(xbus->busname[0] == '\0'); /* No name? */ BUG_ON(xbus->poller); /* Hmmm... overrun pollers? */ XBUS_DBG(xbus, "\n"); poller = kmalloc(sizeof(*poller), GFP_KERNEL); if(!poller) goto err; memset(poller, 0, sizeof(*poller)); poller->xbus = xbus; xbus->poller = poller; /* poll related variables */ atomic_set(&poller->count_poll_answers, 0); atomic_set(&poller->count_xpds_to_initialize, 0); atomic_set(&poller->count_xpds_initialized, 0); INIT_LIST_HEAD(&poller->poll_results); init_waitqueue_head(&poller->wait_for_polls); init_waitqueue_head(&poller->wait_for_xpd_initialization); poller->wq = create_singlethread_workqueue(xbus->busname); if(!poller->wq) { XBUS_ERR(xbus, "Failed to create poller workqueue.\n"); goto err; } return poller; err: poller_destroy(poller); return NULL; } /* * Sends an xbus_poll() work to the poller workqueue of the given xbus. */ static int poller_dispatch(xbus_t *xbus) { struct xbus_poller *poller = xbus->poller; if(!poller) { XBUS_ERR(xbus, "missing poller\n"); return 0; } /* Initialize the work. (adapt to kernel API changes). */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) INIT_WORK(&poller->xpds_init_work, xbus_poll); #else INIT_WORK(&poller->xpds_init_work, xbus_poll, poller); #endif /* Now send it */ if(!queue_work(poller->wq, &poller->xpds_init_work)) { XBUS_ERR(xbus, "Failed to queue xpd initialization work\n"); return 0; } return 1; } void xbus_activate(xbus_t *xbus) { xbus_ops_t *ops; struct xbus_poller *poller; BUG_ON(!xbus); ops = xbus->ops; BUG_ON(!ops); poller = xbus->poller; BUG_ON(!poller); /* Sanity checks */ BUG_ON(!ops->xframe_send); BUG_ON(!ops->xframe_new || !ops->xframe_free); xbus->hardware_exists = 1; XBUS_DBG(xbus, "Activating\n"); /* Poll it */ poller_dispatch(xbus); } void xbus_disconnect(xbus_t *xbus) { int i; BUG_ON(!xbus); XBUS_DBG(xbus, "\n"); xbus->hardware_exists = 0; for(i = 0; i < MAX_XPDS; i++) { xpd_t *xpd = xpd_of(xbus, i); if(!xpd) continue; if(xpd->xbus_idx != i) { XBUS_ERR(xbus, "BUG: xpd->xbus_idx=%d != i=%d\n", xpd->xbus_idx, i); continue; } xpd_disconnect(xpd); } XBUS_DBG(xbus, "Deactivated\n"); if(xbus->open_counter == 0) { xbus_remove(xbus); } } static xbus_t *xbus_alloc(void) { unsigned long flags; xbus_t *xbus; int i; xbus = kmalloc(sizeof(xbus_t), GFP_KERNEL); if(!xbus) { ERR("%s: out of memory\n", __FUNCTION__); return NULL; } memset(xbus, 0, sizeof(xbus_t)); spin_lock_irqsave(&xbuses_lock, flags); for(i = 0; i < MAX_BUSES; i++) if(xbuses_array[i] == NULL) break; if(i >= MAX_BUSES) { ERR("%s: No free slot for new bus. i=%d\n", __FUNCTION__, i); kfree(xbus); return NULL; } /* Found empty slot */ xbuses_array[i] = xbus; xbus->num = i; bus_count++; spin_unlock_irqrestore(&xbuses_lock, flags); return xbus; } static void xbus_free(xbus_t *xbus) { unsigned long flags; if(!xbus) return; spin_lock_irqsave(&xbuses_lock, flags); BUG_ON(!xbus_of(xbus->num)); BUG_ON(xbus != xbus_of(xbus->num)); xbuses_array[xbus->num] = NULL; bus_count--; spin_unlock_irqrestore(&xbuses_lock, flags); #ifdef XPP_DEBUGFS if(xbus->debugfs_dir) { if(xbus->debugfs_file) { XBUS_DBG(xbus, "Removing debugfs file\n"); debugfs_remove(xbus->debugfs_file); } XBUS_DBG(xbus, "Removing debugfs directory\n"); debugfs_remove(xbus->debugfs_dir); } #endif #ifdef CONFIG_PROC_FS if(xbus->proc_xbus_dir) { if(xbus->proc_xbus_summary) { XBUS_DBG(xbus, "Removing proc '%s'\n", PROC_XBUS_SUMMARY); remove_proc_entry(PROC_XBUS_SUMMARY, xbus->proc_xbus_dir); xbus->proc_xbus_summary = NULL; } if(xbus->proc_xbus_waitfor_xpds) { XBUS_DBG(xbus, "Removing proc '%s'\n", PROC_XBUS_WAITFOR_XPDS); remove_proc_entry(PROC_XBUS_WAITFOR_XPDS, xbus->proc_xbus_dir); xbus->proc_xbus_waitfor_xpds = NULL; } #ifdef PROTOCOL_DEBUG if(xbus->proc_xbus_command) { XBUS_DBG(xbus, "Removing proc '%s'\n", PROC_XBUS_COMMAND); remove_proc_entry(PROC_XBUS_COMMAND, xbus->proc_xbus_dir); xbus->proc_xbus_command = NULL; } #endif XBUS_DBG(xbus, "Removing proc directory\n"); remove_proc_entry(xbus->busname, xpp_proc_toplevel); xbus->proc_xbus_dir = NULL; } #endif poller_destroy(xbus->poller); kfree(xbus); } xbus_t *xbus_new(xbus_ops_t *ops) { int err; xbus_t *xbus = NULL; struct xbus_poller *poller; BUG_ON(!ops); xbus = xbus_alloc(); if(!xbus) return NULL; /* Init data structures */ spin_lock_init(&xbus->lock); snprintf(xbus->busname, XBUS_NAMELEN, "XBUS-%02d", xbus->num); XBUS_INFO(xbus, "New xbus\n"); init_waitqueue_head(&xbus->packet_cache_empty); atomic_set(&xbus->packet_counter, 0); atomic_set(&xbus->pcm_nesting, 1); xbus->min_tx_sync = INT_MAX; xbus->min_rx_sync = INT_MAX; xbus->num_xpds = 0; poller = poller_new(xbus); if(!poller) { ERR("Failed to allocate poller\n"); xbus_free(xbus); return NULL; } init_rwsem(&xbus->in_use); xbus_reset_counters(xbus); #ifdef CONFIG_PROC_FS XBUS_DBG(xbus, "Creating xbus proc directory\n"); xbus->proc_xbus_dir = proc_mkdir(xbus->busname, xpp_proc_toplevel); if(!xbus->proc_xbus_dir) { XBUS_ERR(xbus, "Failed to create proc directory\n"); err = -EIO; goto nobus; } xbus->proc_xbus_summary = create_proc_read_entry(PROC_XBUS_SUMMARY, 0444, xbus->proc_xbus_dir, xbus_read_proc, xbus); if (!xbus->proc_xbus_summary) { XBUS_ERR(xbus, "Failed to create proc file '%s'\n", PROC_XBUS_SUMMARY); err = -EIO; goto nobus; } xbus->proc_xbus_summary->owner = THIS_MODULE; xbus->proc_xbus_waitfor_xpds = create_proc_read_entry(PROC_XBUS_WAITFOR_XPDS, 0444, xbus->proc_xbus_dir, xbus_read_waitfor_xpds, xbus); if (!xbus->proc_xbus_waitfor_xpds) { XBUS_ERR(xbus, "Failed to create proc file '%s'\n", PROC_XBUS_WAITFOR_XPDS); err = -EIO; goto nobus; } xbus->proc_xbus_waitfor_xpds->owner = THIS_MODULE; #ifdef PROTOCOL_DEBUG xbus->proc_xbus_command = create_proc_entry(PROC_XBUS_COMMAND, 0200, xbus->proc_xbus_dir); if (!xbus->proc_xbus_command) { XBUS_ERR(xbus, "Failed to create proc file '%s'\n", PROC_XBUS_COMMAND); err = -EIO; goto nobus; } xbus->proc_xbus_command->write_proc = proc_xbus_command_write; 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) { XBUS_ERR(xbus, "Failed to create debugfs directory\n"); 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) { XBUS_ERR(xbus, "Failed to create dchannel file\n"); goto nobus; } #endif /* Sanity checks */ if(!ops->xframe_send) { XBUS_ERR(xbus, "missing mandatory handler: xframe_send\n"); goto nobus; } if(!ops->xframe_new || !ops->xframe_free) { XBUS_NOTICE(xbus, "Using default packet allocators\n"); ops->xframe_new = xbus_xframe_new; ops->xframe_free = xbus_xframe_free; } xbus->ops = ops; return xbus; nobus: xbus_free(xbus); return NULL; } void xbus_remove(xbus_t *xbus) { int i; int ret; BUG_ON(!xbus); if(!xbus_of(xbus->num)) { DBG("XBUS #%d was already removed. Skip.\n", xbus->num); return; } XBUS_DBG(xbus, "\n"); /* Block until no one use */ down_write(&xbus->in_use); XBUS_INFO(xbus, "Removing xbus\n"); xbus_sysfs_remove(xbus); /* Device-Model */ for(i = 0; i < MAX_XPDS; i++) { xpd_t *xpd = xpd_of(xbus, i); if(xpd) { if(xpd->xbus_idx != i) { XBUS_ERR(xbus, "BUG: xpd->xbus_idx=%d != i=%d\n", xpd->xbus_idx, i); continue; } XBUS_DBG(xbus, " Removing xpd #%d\n", i); xpd_remove(xpd); } xbus->xpds[i] = NULL; } ret = wait_event_interruptible(xbus->packet_cache_empty, atomic_read(&xbus->packet_counter) == 0); if(ret) { XBUS_ERR(xbus, "waiting for packet_cache_empty interrupted!!!\n"); } xbus_free(xbus); } /*------------------------- Proc handling --------------------------*/ void xbus_reset_counters(xbus_t *xbus) { int i; XBUS_DBG(xbus, "Reseting counters\n"); for(i = 0; i < XBUS_COUNTER_MAX; i++) { xbus->counters[i] = 0; } } #if CONFIG_PROC_FS static int xbus_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { xbus_t *xbus = data; struct xbus_poller *poller; unsigned long flags; int len = 0; int i; if(!xbus) goto out; spin_lock_irqsave(&xbus->lock, flags); poller = xbus->poller; len += sprintf(page + len, "%s: CONNECTOR=%s STATUS=%s bus_type=%d\n", xbus->busname, xbus->busdesc, (xbus->hardware_exists) ? "connected" : "missing", xbus->bus_type ); len += sprintf(page + len, "POLLS: %d/%d\n", atomic_read(&poller->count_poll_answers), MAX_XPDS); len += sprintf(page + len, "XPDS_READY: %d/%d\n", atomic_read(&poller->count_xpds_initialized), atomic_read(&poller->count_xpds_to_initialize)); len += sprintf(page + len, "\nmax_packet_size=%d open_counter=%d packet_count=%d\n", xbus->max_packet_size, xbus->open_counter, atomic_read(&xbus->packet_counter) ); len += sprintf(page + len, "\nPLL DRIFT=%d\n", xbus->sync_adjustment); len += sprintf(page + len, "PCM Metrices:\n"); len += sprintf(page + len, "\tPCM TX: min=%ld max=%ld\n", xbus->min_tx_sync, xbus->max_tx_sync); len += sprintf(page + len, "\tPCM RX: min=%ld max=%ld\n", xbus->min_rx_sync, xbus->max_rx_sync); len += sprintf(page + len, "COUNTERS:\n"); for(i = 0; i < XBUS_COUNTER_MAX; i++) { len += sprintf(page + len, "\t%-15s = %d\n", xbus_counters[i].name, xbus->counters[i]); } len += sprintf(page + len, "<-- len=%d\n", len); /* reset statistics */ xbus->min_tx_sync = INT_MAX; xbus->max_tx_sync = 0; xbus->min_rx_sync = INT_MAX; xbus->max_rx_sync = 0; spin_unlock_irqrestore(&xbus->lock, flags); out: if (len <= off+count) *eof = 1; *start = page + off; len -= off; if (len > count) len = count; if (len < 0) len = 0; return len; } static int xbus_read_waitfor_xpds(char *page, char **start, off_t off, int count, int *eof, void *data) { int len = 0; unsigned long flags; xbus_t *xbus = data; struct xbus_poller *poller; int ret; if(!xbus) goto out; poller = xbus->poller; XBUS_DBG(xbus, "Waiting for card initialization of %d XPD's max %d seconds\n", MAX_XPDS, INITIALIZATION_TIMEOUT/HZ); /* * xbus_poll sets count_xpds_to_initialize only when polling is finished. * To prevent race conditions we test both: * - It is none zero -- meaning we already have the poll results. * - And all units have finished initialization. */ ret = wait_event_interruptible_timeout(poller->wait_for_xpd_initialization, atomic_read(&poller->count_xpds_to_initialize) && atomic_read(&poller->count_xpds_initialized) >= atomic_read(&poller->count_xpds_to_initialize), INITIALIZATION_TIMEOUT); if(ret == 0) { XBUS_ERR(xbus, "Card Initialization Timeout\n"); return ret; } else if(ret < 0) { XBUS_ERR(xbus, "Card Initialization Interrupted %d\n", ret); return ret; } XBUS_DBG(xbus, "Finished initialization of %d XPD's in %d seconds.\n", MAX_XPDS, (INITIALIZATION_TIMEOUT - ret)/HZ); spin_lock_irqsave(&xbus->lock, flags); len += sprintf(page + len, "XPDS_READY: %s: %d/%d\n", xbus->busname, atomic_read(&poller->count_xpds_initialized), atomic_read(&poller->count_xpds_to_initialize)); spin_unlock_irqrestore(&xbus->lock, flags); out: if (len <= off+count) *eof = 1; *start = page + off; len -= off; if (len > count) len = count; if (len < 0) len = 0; return len; } #ifdef PROTOCOL_DEBUG static int proc_xbus_command_write(struct file *file, const char __user *buffer, unsigned long count, void *data) { char buf[MAX_PROC_WRITE]; xbus_t *xbus = data; xpacket_t *pack; char *p; byte *pack_start; byte *q; xframe_t *xframe; if(count >= MAX_PROC_WRITE) { XBUS_ERR(xbus, "%s: line too long\n", __FUNCTION__); return -EFBIG; } if(copy_from_user(buf, buffer, count)) return -EINVAL; buf[count] = '\0'; q = pack_start = buf; for(p = buf; *p;) { int val; char hexdigit[3]; while(*p && isspace(*p)) // skip whitespace p++; if(!(*p)) break; if(!isxdigit(*p)) { ERR("%s: %s: bad hex value ASCII='0x%X' at position %d\n", __FUNCTION__, xbus->busname, *p, p - buf); return -EINVAL; } hexdigit[0] = *p++; hexdigit[1] = '\0'; hexdigit[2] = '\0'; if(isxdigit(*p)) hexdigit[1] = *p++; if(sscanf(hexdigit, "%2X", &val) != 1) { ERR("%s: %s: bad hex value '%s' at position %d\n", __FUNCTION__, xbus->busname, hexdigit, p - buf); return -EINVAL; } *q++ = val; // XBUS_DBG(xbus, "'%s' val=%d\n", hexdigit, val); } 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? */ send_cmd_frame(xbus, xframe); return count; } #endif static int read_proc_xbuses(char *page, char **start, off_t off, int count, int *eof, void *data) { int len = 0; unsigned long flags; int i; spin_lock_irqsave(&xbuses_lock, flags); for(i = 0; i < MAX_BUSES; i++) { xbus_t *xbus = xbus_of(i); if(xbus) { len += sprintf(page + len, "%s: CONNECTOR=%s STATUS=%s bus_type=%d\n", xbus->busname, xbus->busdesc, (xbus->hardware_exists) ? "connected" : "missing", xbus->bus_type ); } } #if 0 len += sprintf(page + len, "<-- len=%d\n", len); #endif spin_unlock_irqrestore(&xbuses_lock, flags); if (len <= off+count) *eof = 1; *start = page + off; len -= off; if (len > count) len = count; if (len < 0) len = 0; return len; } static int read_proc_xmitter(char *page, char **start, off_t off, int count, int *eof, void *data) { int len = 0; len += sprintf(page + len, "command_count: %d\n", command_count); len += sprintf(page + len, "sum_latency: %ld\n", sum_latency); if(command_count) len += sprintf(page + len, "average latency: %ld\n", sum_latency/command_count); #if 0 len += sprintf(page + len, "<-- len=%d\n", len); #endif if (len <= off+count) *eof = 1; *start = page + off; len -= off; if (len > count) len = count; if (len < 0) len = 0; return len; } #endif /*------------------------- Initialization -------------------------*/ static void xbus_core_cleanup(void) { #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"); remove_proc_entry(PROC_XBUSES, xpp_proc_toplevel); proc_xbuses = NULL; } #endif #ifdef CONFIG_PROC_FS if(proc_commands) { DBG("Removing %s from proc\n", PROC_CMDS); remove_proc_entry(PROC_CMDS, xpp_proc_toplevel); proc_commands = NULL; } #endif if(xframes_cache) kmem_cache_destroy(xframes_cache); } int __init xbus_core_init(void) { int ret = 0; #ifdef PROTOCOL_DEBUG INFO("FEATURE: with PROTOCOL_DEBUG\n"); #endif #ifdef XPP_DEBUGFS INFO("FEATURE: with XPP_DEBUGFS support\n"); #endif xframes_cache = kmem_cache_create("xpp_frames", sizeof(xframe_t) + XFRAME_DATASIZE, 0, 0, NULL, NULL); if(!xframes_cache) { return -ENOMEM; } #ifdef CONFIG_PROC_FS proc_commands = create_proc_read_entry( PROC_CMDS, 0444, xpp_proc_toplevel, read_proc_xmitter, NULL); if (!proc_commands) { ERR("Failed to create proc file %s\n", PROC_CMDS); goto err; } proc_commands->owner = THIS_MODULE; #endif #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); 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) { ERR("Failed to create debugfs root\n"); ret = -EFAULT; goto err; } #endif if((ret = register_xpp_bus()) < 0) goto err; return 0; err: xbus_core_cleanup(); return ret; } void __exit xbus_core_shutdown(void) { int i; for(i = 0; i < MAX_BUSES; i++) { xbus_t *xbus = xbus_of(i); if(xbus) xbus_remove(xbus); } BUG_ON(bus_count); unregister_xpp_bus(); xbus_core_cleanup(); } EXPORT_SYMBOL(xpd_of); EXPORT_SYMBOL(xpd_byaddr); EXPORT_SYMBOL(xbus_new); 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); EXPORT_SYMBOL(xframe_send); EXPORT_SYMBOL(send_cmd_frame); EXPORT_SYMBOL(xbus_poller_notify); #ifdef XPP_DEBUGFS EXPORT_SYMBOL(xbus_log); #endif