/* * 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$"; /* Command line parameters */ extern int print_dbg; /* Kernel versions... */ /* * Hotplug replaced with uevent in 2.6.16 */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) #define OLD_HOPLUG_SUPPORT // for older kernels #endif #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 /*--------- Sysfs Bus handling ----*/ static int xpp_bus_match(struct device *dev, struct device_driver *driver) { DBG(GENERAL, "dev->bus_id = %s, driver->name = %s\n", dev->bus_id, driver->name); return 1; } #ifdef OLD_HOPLUG_SUPPORT static int xpp_bus_hotplug(struct device *dev, char **envp, int envnum, char *buff, int bufsize) { xbus_t *xbus; if(!dev) return -ENODEV; xbus = dev_to_xbus(dev); envp[0] = buff; if(snprintf(buff, bufsize, "XBUS_NAME=%s", xbus->busname) >= bufsize) return -ENOMEM; envp[1] = NULL; return 0; } #else #define XBUS_ADD_UEVENT_VAR(fmt, val...) \ do { \ int err = add_uevent_var(envp, num_envp, &i, \ buffer, buffer_size, &len, \ fmt, val); \ if (err) \ return err; \ } while (0) static int xpp_bus_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size) { xbus_t *xbus; int i = 0; int len = 0; if(!dev) return -ENODEV; xbus = dev_to_xbus(dev); DBG(GENERAL, "bus_id=%s xbus=%s\n", dev->bus_id, xbus->busname); XBUS_ADD_UEVENT_VAR("XBUS_NUM=%02d", xbus->num); XBUS_ADD_UEVENT_VAR("XBUS_NAME=%s", xbus->busname); envp[i] = NULL; return 0; } #endif static void xpp_bus_release(struct device *dev) { DBG(GENERAL, "\n"); } static void xpp_dev_release(struct device *dev) { xbus_t *xbus; BUG_ON(!dev); xbus = dev_to_xbus(dev); XBUS_DBG(GENERAL, xbus, "\n"); } static struct bus_type xpp_bus_type = { .name = "astribanks", .match = xpp_bus_match, #ifdef OLD_HOPLUG_SUPPORT .hotplug = xpp_bus_hotplug, #else .uevent = xpp_bus_uevent, #endif }; static struct device xpp_bus = { .bus_id = "xppbus", .release = xpp_bus_release }; static struct device_driver xpp_driver = { .name = "xppdrv", .bus = &xpp_bus_type, #ifndef OLD_HOPLUG_SUPPORT .owner = THIS_MODULE #endif }; int register_xpp_bus(void) { int ret; if((ret = bus_register(&xpp_bus_type)) < 0) { ERR("%s: bus_register failed. Error number %d", __FUNCTION__, ret); goto failed_bus; } if((ret = device_register(&xpp_bus)) < 0) { ERR("%s: registration of xpp_bus failed. Error number %d", __FUNCTION__, ret); goto failed_busdevice; } if((ret = driver_register(&xpp_driver)) < 0) { ERR("%s: driver_register failed. Error number %d", __FUNCTION__, ret); goto failed_driver; } return 0; failed_driver: device_unregister(&xpp_bus); failed_busdevice: bus_unregister(&xpp_bus_type); failed_bus: return ret; } void unregister_xpp_bus(void) { driver_unregister(&xpp_driver); device_unregister(&xpp_bus); bus_unregister(&xpp_bus_type); } /*--------- Sysfs Device handling ----*/ static DEVICE_ATTR_FUNC(connector_show, dev, buf) { xbus_t *xbus; int ret; xbus = dev_to_xbus(dev); 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_to_xbus(dev); ret = snprintf(buf, PAGE_SIZE, "%s\n", (xbus->hardware_exists)?"connected":"missing"); return ret; } static DEVICE_ATTR(connector, S_IRUGO, connector_show, NULL); static DEVICE_ATTR(status, S_IRUGO, status_show, NULL); void xbus_sysfs_remove(xbus_t *xbus) { struct device *astribank; BUG_ON(!xbus); XBUS_DBG(GENERAL, xbus, "\n"); astribank = &xbus->astribank; BUG_ON(!astribank); device_remove_file(&xbus->astribank, &dev_attr_status); device_remove_file(&xbus->astribank, &dev_attr_connector); device_unregister(&xbus->astribank); } int xbus_sysfs_create(xbus_t *xbus) { struct device *astribank; int ret = 0; BUG_ON(!xbus); astribank = &xbus->astribank; BUG_ON(!astribank); XBUS_DBG(GENERAL, xbus, "\n"); device_initialize(astribank); astribank->bus = &xpp_bus_type; astribank->parent = &xpp_bus; snprintf(astribank->bus_id, BUS_ID_SIZE, "xbus-%02d", xbus->num); astribank->driver_data = NULL; /* FIXME: add some usefull data */ astribank->release = xpp_dev_release; ret = device_register(astribank); if(ret) { XBUS_ERR(xbus, "%s: device_add failed: %d\n", __FUNCTION__, ret); goto out; } ret = device_create_file(astribank, &dev_attr_connector); if(ret) { XBUS_ERR(xbus, "%s: device_create_file failed: %d\n", __FUNCTION__, ret); goto out; } ret = device_create_file(astribank, &dev_attr_status); if(ret) { XBUS_ERR(xbus, "%s: device_create_file failed: %d\n", __FUNCTION__, ret); goto out; } out: return ret; }