/* * 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 /* for msleep() to debug */ #include "xpd.h" #include "xpp_zap.h" #include "xbus-core.h" #include "zap_debug.h" static const char rcsid[] = "$Id$"; /* Defines */ #define POLL_TIMEOUT (2*MAX_XPDS) /* in jiffies */ #define INITIALIZATION_TIMEOUT (40*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,"Timeout (in jiffies) waiting for units to reply (default " __stringify(POLL_TIMEOUT) ")"); /* Forward declarations */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14) #define DEVICE_ATTR_FUNC(name,dev,buf) \ ssize_t name(struct device *dev, struct device_attribute *attr, char *buf) #else #define DEVICE_ATTR_FUNC(name,dev,buf) \ ssize_t name(struct device *dev, char *buf) #endif static DEVICE_ATTR_FUNC(connector_show, dev, buf); static DEVICE_ATTR_FUNC(status_show, dev, buf); static int xbus_poll(void *data); static void xbus_release(struct device *dev); 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); /* Data structures */ struct workqueue_struct *xpp_worker = NULL; 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; 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 */ xpacket_t *xbus_packet_new(xbus_t *xbus, gfp_t flags) { xpacket_t *pack; /* 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)); } return pack; } void xbus_packet_free(xbus_t *xbus, xpacket_t *p) { 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)); } /*------------------------- 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(xbus_t *xbus, int xpd_num) { if(!VALID_XPD_NUM(xpd_num)) return NULL; return xbus->xpds[xpd_num]; } int xbus_register_xpd(xbus_t *xbus, xpd_t *xpd) { unsigned int xpd_num = xpd->id; unsigned long flags; int ret = 0; spin_lock_irqsave(&xbus->lock, flags); if(!VALID_XPD_NUM(xpd_num)) { ERR("%s: Bad xpd_num = %d\n", xbus->busname, xpd_num); ret = -EINVAL; goto out; } if(xbus->xpds[xpd_num] != NULL) { xpd_t *other = xbus->xpds[xpd_num]; ERR("%s: xpd_num=%d is occupied by %p (%s)\n", xbus->busname, xpd_num, other, other->xpdname); ret = -EINVAL; goto out; } xbus->xpds[xpd_num] = xpd; 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->id; unsigned long flags; int ret = 0; spin_lock_irqsave(&xbus->lock, flags); if(!VALID_XPD_NUM(xpd_num)) { ERR("%s: Bad xpd_num = %d\n", xbus->busname, xpd_num); ret = -EINVAL; goto out; } if(xbus->xpds[xpd_num] != xpd) { xpd_t *other = xbus->xpds[xpd_num]; ERR("%s: xpd_num=%d is occupied by %p (%s)\n", xbus->busname, xpd_num, other, other->xpdname); ret = -EINVAL; goto out; } xbus->xpds[xpd_num] = NULL; xbus->num_xpds--; xpd->xbus = NULL; 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. */ static int xbus_poll(void *data) { 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; int xpd_num; xbus_t *xbus = data; if(!down_read_trylock(&xbus->in_use)) { ERR("%s is being removed...\n", xbus->busname); return -EBUSY; } msleep(2); /* roundtrip for older polls */ spin_lock_irqsave(&xbus->lock, flags); DBG("%s\n", xbus->busname); /* * Send out the polls */ for(id = 0; id < MAX_XPDS; id++) { if(!xbus->hardware_exists) break; // DBG(" Polling slot %d %s\n", id, xbus->busname); spin_unlock_irqrestore(&xbus->lock, flags); ret = CALL_PROTO(GLOBAL, DESC_REQ, xbus, NULL, id); spin_lock_irqsave(&xbus->lock, flags); if(ret < 0) { ERR("xpp: %s: Failed sending DESC_REQ to XPD #%d\n", __FUNCTION__, id); goto out; } mdelay(1); /* FIXME: debugging for Dima */ } spin_unlock_irqrestore(&xbus->lock, flags); /* * Wait for replies */ DBG("%s: Polled %d XPD's. Waiting for replies max %d jiffies\n", xbus->busname, MAX_XPDS, poll_timeout); ret = wait_event_interruptible_timeout(xbus->wait_for_polls, atomic_read(&xbus->count_poll_answers) >= MAX_XPDS, poll_timeout); if(ret == 0) { ERR("%s: Poll timeout. Continuing anyway.\n", xbus->busname); /* * Continue processing. Maybe some units did reply. */ } else if(ret < 0) { ERR("%s: Poll interrupted %d\n", xbus->busname, ret); goto out; } else DBG("%s: Poll finished in %d jiffies.\n", xbus->busname, 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, &xbus->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_num = xpd_addr2num(&card_desc->xpd_addr); xpd = xpd_of(xbus, xpd_num); 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(&xbus->count_xpds_to_initialize, count_added); spin_unlock_irqrestore(&xbus->lock, flags); INFO("%s: Poll results: removals=%d additions=%d\n", xbus->busname, 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_num = xpd_addr2num(&card_desc->xpd_addr); xpd = xpd_of(xbus, xpd_num); 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(&xbus->count_xpds_initialized); } wake_up(&xbus->wait_for_xpd_initialization); out: up_read(&xbus->in_use); return ret; } void xbus_activate(xbus_t *xbus) { xbus_ops_t *ops; BUG_ON(!xbus); ops = xbus->ops; BUG_ON(!ops); /* Sanity checks */ BUG_ON(!ops->packet_send); BUG_ON(!ops->packet_new || !ops->packet_free); xbus->hardware_exists = 1; DBG("Activating: %s\n", xbus->busname); /* Poll it */ INIT_WORK(&xbus->xpds_init_work, (void (*)(void *))xbus_poll, (void *)xbus); if(!queue_work(xpp_worker, &xbus->xpds_init_work)) { ERR("Failed to queue xpd initialization work\n"); /* FIXME: need to return error */ } } void xbus_disconnect(xbus_t *xbus) { int i; BUG_ON(!xbus); DBG("%s\n", xbus->busname); xbus->hardware_exists = 0; for(i = 0; i < MAX_XPDS; i++) { xpd_t *xpd = xpd_of(xbus, i); if(!xpd) continue; if(xpd->id != i) { ERR("%s: BUG: xpd->id=%d != i=%d\n", __FUNCTION__, xpd->id, i); continue; } xpd_disconnect(xpd); } DBG("%s (deactivated)\n", xbus->busname); 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 CONFIG_PROC_FS if(xbus->proc_xbus_dir) { if(xbus->proc_xbus_summary) { DBG("Removing proc '%s' for %s\n", PROC_XBUS_SUMMARY, xbus->busname); remove_proc_entry(PROC_XBUS_SUMMARY, xbus->proc_xbus_dir); xbus->proc_xbus_summary = NULL; } if(xbus->proc_xbus_waitfor_xpds) { DBG("Removing proc '%s' for %s\n", PROC_XBUS_WAITFOR_XPDS, xbus->busname); 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) { DBG("Removing proc '%s' for %s\n", PROC_XBUS_COMMAND, xbus->busname); remove_proc_entry(PROC_XBUS_COMMAND, xbus->proc_xbus_dir); xbus->proc_xbus_command = NULL; } #endif DBG("Removing proc directory %s\n", xbus->busname); remove_proc_entry(xbus->busname, xpp_proc_toplevel); xbus->proc_xbus_dir = NULL; } #endif device_remove_file(&xbus->the_bus, &dev_attr_status); device_remove_file(&xbus->the_bus, &dev_attr_connector); device_unregister(&xbus->the_bus); kfree(xbus); } static void xbus_release(struct device *dev) { xbus_t *xbus; BUG_ON(!dev); xbus = dev->driver_data; DBG("%s\n", xbus->busname); } xbus_t *xbus_new(xbus_ops_t *ops) { int err; xbus_t *xbus = NULL; BUG_ON(!ops); xbus = xbus_alloc(); if(!xbus) return NULL; /* Init data structures */ spin_lock_init(&xbus->lock); snprintf(xbus->busname, XBUS_NAMELEN, "XBUS-%d", xbus->num); INFO("New xbus: %s\n", xbus->busname); init_waitqueue_head(&xbus->packet_cache_empty); atomic_set(&xbus->packet_counter, 0); /* poll related variables */ atomic_set(&xbus->count_poll_answers, 0); atomic_set(&xbus->count_xpds_to_initialize, 0); atomic_set(&xbus->count_xpds_initialized, 0); INIT_LIST_HEAD(&xbus->poll_results); init_waitqueue_head(&xbus->wait_for_polls); init_waitqueue_head(&xbus->wait_for_xpd_initialization); xbus->num_xpds = 0; init_rwsem(&xbus->in_use); xbus_reset_counters(xbus); /* Device-Model */ snprintf(xbus->the_bus.bus_id, BUS_ID_SIZE, "xbus-%d", xbus->num); xbus->the_bus.driver_data = xbus; xbus->the_bus.release = xbus_release; err = device_register(&xbus->the_bus); if(err) { ERR("%s: device_register failed: %d\n", __FUNCTION__, err); goto nobus; } err = device_create_file(&xbus->the_bus, &dev_attr_connector); if(err) { ERR("%s: device_create_file failed: %d\n", __FUNCTION__, err); goto nobus; } err = device_create_file(&xbus->the_bus, &dev_attr_status); if(err) { ERR("%s: device_create_file failed: %d\n", __FUNCTION__, err); goto nobus; } #ifdef CONFIG_PROC_FS DBG("Creating xbus proc directory %s.\n",xbus->busname); xbus->proc_xbus_dir = proc_mkdir(xbus->busname, xpp_proc_toplevel); if(!xbus->proc_xbus_dir) { ERR("Failed to create proc directory for xbus %s\n", xbus->busname); 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) { ERR("Failed to create '%s' proc file for xbus %s\n", PROC_XBUS_SUMMARY, xbus->busname); 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) { ERR("Failed to create '%s' proc file for xbus %s\n", PROC_XBUS_WAITFOR_XPDS, xbus->busname); 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) { ERR("Failed to create '%s' proc file for xbus %s\n", PROC_XBUS_COMMAND, xbus->busname); 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 /* Sanity checks */ if(!ops->packet_send) { ERR("%s: missing mandatory handler: packet_send\n", __FUNCTION__); goto nobus; } if(!ops->packet_new || !ops->packet_free) { NOTICE("%s: Using default packet allocators\n", __FUNCTION__); ops->packet_new = xbus_packet_new; ops->packet_free = xbus_packet_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; } DBG("%s\n", xbus->busname); /* Block until no one use */ down_write(&xbus->in_use); INFO("Removing xbus(%d) %s\n", xbus->num, xbus->busname); for(i = 0; i < MAX_XPDS; i++) { xpd_t *xpd = xpd_of(xbus, i); if(xpd) { if(xpd->id != i) { ERR("%s: BUG: xpd->id=%d != i=%d\n", __FUNCTION__, xpd->id, i); continue; } DBG(" Removing xpd id=%d\n", xpd->id); xpd_remove(xpd); } xbus->xpds[i] = NULL; } ret = wait_event_interruptible(xbus->packet_cache_empty, atomic_read(&xbus->packet_counter) == 0); if(ret) { ERR("waiting for packet_cache_empty interrupted!!!\n"); } xbus_free(xbus); } /*------------------------- Proc handling --------------------------*/ void xbus_reset_counters(xbus_t *xbus) { int i; DBG("Reseting counters of %s\n", xbus->busname); 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) { int len = 0; unsigned long flags; xbus_t *xbus = data; int i; if(!xbus) goto out; spin_lock_irqsave(&xbus->lock, flags); 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(&xbus->count_poll_answers), MAX_XPDS); len += sprintf(page + len, "XPDS_READY: %d/%d\n", atomic_read(&xbus->count_xpds_initialized), atomic_read(&xbus->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, "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); 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; int ret; if(!xbus) goto out; DBG("%s: Waiting for card initialization of %d XPD's max %d seconds\n", xbus->busname, 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(xbus->wait_for_xpd_initialization, atomic_read(&xbus->count_xpds_to_initialize) && atomic_read(&xbus->count_xpds_initialized) >= atomic_read(&xbus->count_xpds_to_initialize), INITIALIZATION_TIMEOUT); if(ret == 0) { ERR("%s: Card Initialization Timeout\n", xbus->busname); return ret; } else if(ret < 0) { ERR("%s: Card Initialization Interrupted %d\n", xbus->busname, ret); return ret; } 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", atomic_read(&xbus->count_xpds_initialized), atomic_read(&xbus->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_contents; byte *q; if(count >= MAX_PROC_WRITE) { ERR("%s: line too long\n", __FUNCTION__); return -EFBIG; } 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; 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); goto err; } 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); goto err; } *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); return count; err: xbus->ops->packet_free(xbus, pack); return -EINVAL; } #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; } #endif /*------------------------- Initialization -------------------------*/ static DEVICE_ATTR_FUNC(connector_show, dev, buf) { xbus_t *xbus; int ret; xbus = dev->driver_data; ret = snprintf(buf, PAGE_SIZE, "%s\n", xbus->busdesc); return ret; } static DEVICE_ATTR_FUNC(status_show, dev, buf) { xbus_t *xbus; int ret; xbus = dev->driver_data; ret = snprintf(buf, PAGE_SIZE, "%s\n", (xbus->hardware_exists)?"connected":"missing"); return ret; } static int xbus_match(struct device *dev, struct device_driver *driver) { DBG("dev->bus_id = %s, driver->name = %s\n", dev->bus_id, driver->name); return strncmp(dev->bus_id, driver->name, strlen(driver->name)) == 0; } #if 0 /* Hotplug replaced with uevent in 2.6.16 */ static int xbus_hotplug(struct device *device, char **envp, int envnum, char *buff, int bufsize) { envp[0] = buff; if(snprintf(buff, bufsize, "XBUS_VERSION=%s", revision) >= bufsize) return -ENOMEM; envp[1] = NULL; return 0; } #endif struct bus_type xbus_bus_type = { .name = "xbus", .match = xbus_match, /* FIXME: Hotplug replaced with uevent in 2.6.16 */ #if 0 .hotplug = xbus_hotplug, #endif }; static void xbus_core_cleanup(void) { if (xpp_worker) { flush_workqueue(xpp_worker); destroy_workqueue(xpp_worker); xpp_worker = NULL; } #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 if(packet_cache) kmem_cache_destroy(packet_cache); } int __init xbus_core_init(void) { int ret; #ifdef PROTOCOL_DEBUG INFO("FEATURE: %s with PROTOCOL_DEBUG\n", THIS_MODULE->name); #endif packet_cache = kmem_cache_create("xpp_packets", sizeof(xpacket_t), 0, 0, NULL, NULL); if(!packet_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; } #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; } proc_xbuses->owner = THIS_MODULE; #endif ret = bus_register(&xbus_bus_type); if(ret) { ERR("%s: bus_register failed. Error number %d", __FUNCTION__, ret); xbus_core_cleanup(); return ret; } return 0; } 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); bus_unregister(&xbus_bus_type); xbus_core_cleanup(); } EXPORT_SYMBOL(xpd_of); EXPORT_SYMBOL(xbus_new); EXPORT_SYMBOL(xbus_remove); EXPORT_SYMBOL(xbus_activate); EXPORT_SYMBOL(xbus_disconnect); EXPORT_SYMBOL(xbus_reset_counters);