diff options
Diffstat (limited to 'package')
-rw-r--r-- | package/broadcom-diag/Makefile (renamed from package/diag/Makefile) | 17 | ||||
-rw-r--r-- | package/broadcom-diag/src/Makefile (renamed from package/diag/src/Makefile) | 1 | ||||
-rw-r--r-- | package/broadcom-diag/src/diag.c | 939 | ||||
-rw-r--r-- | package/diag/src/diag.c | 284 |
4 files changed, 945 insertions, 296 deletions
diff --git a/package/diag/Makefile b/package/broadcom-diag/Makefile index c182321cc1..9ab377a4f8 100644 --- a/package/diag/Makefile +++ b/package/broadcom-diag/Makefile @@ -9,20 +9,21 @@ include $(TOPDIR)/rules.mk include $(INCLUDE_DIR)/kernel.mk -PKG_NAME:=kmod-diag +PKG_NAME:=broadcom-diag PKG_RELEASE:=1 PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/$(PKG_NAME) include $(INCLUDE_DIR)/package.mk -define Package/kmod-diag +define KernelPackage/diag SECTION:=kernel CATEGORY:=Kernel drivers DEFAULT:=y - DEPENDS:=@LINUX_2_4_BRCM + DEPENDS:=@LINUX_2_4_BRCM||LINUX_2_6_BRCM TITLE:=Driver for router LEDs and Buttons - VERSION:=$(LINUX_VERSION)-$(BOARD)-$(PKG_RELEASE) + VERSION:=$(PKG_RELEASE)+$(LINUX_VERSION)-$(BOARD)-$(LINUX_RELEASE) + FILES:=$(PKG_BUILD_DIR)/diag.$(LINUX_KMOD_SUFFIX) endef define Build/Prepare @@ -39,10 +40,4 @@ define Build/Compile modules endef -define Package/kmod-diag/install - install -d -m0755 $(1)/lib/modules/$(LINUX_VERSION) - $(CP) $(PKG_BUILD_DIR)/diag.$(LINUX_KMOD_SUFFIX) \ - $(1)/lib/modules/$(LINUX_VERSION)/ -endef - -$(eval $(call BuildPackage,kmod-diag)) +$(eval $(call KernelPackage,diag)) diff --git a/package/diag/src/Makefile b/package/broadcom-diag/src/Makefile index 169f2e9725..a43f5f3043 100644 --- a/package/diag/src/Makefile +++ b/package/broadcom-diag/src/Makefile @@ -13,7 +13,6 @@ obj-m := diag.o ifeq ($(MAKING_MODULES),1) -export-objs := diag.o -include $(TOPDIR)/Rules.make endif diff --git a/package/broadcom-diag/src/diag.c b/package/broadcom-diag/src/diag.c new file mode 100644 index 0000000000..b7c67314f7 --- /dev/null +++ b/package/broadcom-diag/src/diag.c @@ -0,0 +1,939 @@ +/* + * diag.c - GPIO interface driver for Broadcom boards + * + * Copyright (C) 2006 Mike Baker <mbm@openwrt.org>, + * Felix Fietkau <nbd@openwrt.org> + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Id:$ + */ +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/kmod.h> +#include <linux/proc_fs.h> +#include <linux/timer.h> +#include <linux/version.h> +#include <asm/uaccess.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +#include <linux/kobject.h> +#include <linux/workqueue.h> +#define hotplug_path uevent_helper +#else +#include <linux/tqueue.h> +#define INIT_WORK INIT_TQUEUE +#define schedule_work schedule_task +#define work_struct tq_struct +#define sbh bcm947xx_sbh +#define sbh_lock bcm947xx_sbh_lock +#endif + +#include <typedefs.h> +#include <osl.h> +#include <bcmdevs.h> +#include <sbutils.h> +#include <sbconfig.h> +#include <sbchipc.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +#include <sbmips.h> +#else +#include <hndcpu.h> +#endif + +#define MODULE_NAME "diag" + +#define MAX_GPIO 8 +#define FLASH_TIME HZ/6 + +#define EXTIF_ADDR 0x1f000000 +#define EXTIF_UART (EXTIF_ADDR + 0x00800000) + +/* For LEDs */ +#define GPIO_TYPE_NORMAL (0x0 << 24) +#define GPIO_TYPE_EXTIF (0x1 << 24) +#define GPIO_TYPE_MASK (0xf << 24) + +static unsigned int gpiomask = 0; +module_param(gpiomask, int, 0644); + +enum polarity_t { + REVERSE = 0, + NORMAL = 1, +}; + +enum { + PROC_BUTTON, + PROC_LED, + PROC_MODEL, + PROC_GPIOMASK +}; + +struct prochandler_t { + int type; + void *ptr; +}; + +struct button_t { + struct prochandler_t proc; + char *name; + u16 gpio; + u8 polarity; + u8 pressed; + unsigned long seen; +}; + +struct led_t { + struct prochandler_t proc; + char *name; + u32 gpio; + u8 polarity; + u8 flash; + u8 state; +}; + +struct platform_t { + char *name; + struct button_t buttons[MAX_GPIO]; + struct led_t leds[MAX_GPIO]; +}; + +enum { + /* Linksys */ + WAP54GV1, + WAP54GV3, + WRT54GV1, + WRT54G, + WRTSL54GS, + WRT54G3G, + + /* ASUS */ + WLHDD, + WL300G, + WL500G, + WL500GD, + WL500GP, + ASUS_4702, + + /* Buffalo */ + WBR2_G54, + WHR_G54S, + WHR_HP_G54, + WHR2_A54G54, + WLA2_G54L, + BUFFALO_UNKNOWN, + BUFFALO_UNKNOWN_4710, + + /* Siemens */ + SE505V1, + SE505V2, + + /* US Robotics */ + USR5461, + + /* Dell */ + TM2300, + + /* Motorola */ + WE800G, + WR850GV1, + WR850GV2, + + /* Belkin */ + BELKIN_UNKNOWN, +}; + +static struct platform_t platforms[] = { + /* Linksys */ + [WAP54GV1] = { + .name = "Linksys WAP54G V1", + .buttons = { + { .name = "reset", .gpio = 1 << 0 }, + }, + .leds = { + { .name = "diag", .gpio = 1 << 3 }, + { .name = "wlan", .gpio = 1 << 4 }, + }, + }, + [WAP54GV3] = { + .name = "Linksys WAP54G V3", + .buttons = { + /* FIXME: verify this */ + { .name = "reset", .gpio = 1 << 7 }, + { .name = "ses", .gpio = 1 << 0 }, + }, + .leds = { + /* FIXME: diag? */ + { .name = "ses", .gpio = 1 << 1 }, + }, + }, + [WRT54GV1] = { + .name = "Linksys WRT54G V1.x", + .buttons = { + { .name = "reset", .gpio = 1 << 6 }, + }, + .leds = { + { .name = "diag", .gpio = 0x13 | GPIO_TYPE_EXTIF, .polarity = NORMAL }, + { .name = "dmz", .gpio = 0x12 | GPIO_TYPE_EXTIF, .polarity = NORMAL }, + }, + }, + [WRT54G] = { + .name = "Linksys WRT54G*", + .buttons = { + { .name = "reset", .gpio = 1 << 6 }, + { .name = "ses", .gpio = 1 << 4 }, + }, + .leds = { + { .name = "power", .gpio = 1 << 1, .polarity = NORMAL }, + { .name = "dmz", .gpio = 1 << 7, .polarity = REVERSE }, + { .name = "ses_white", .gpio = 1 << 2, .polarity = REVERSE }, + { .name = "ses_orange", .gpio = 1 << 3, .polarity = REVERSE }, + { .name = "wlan", .gpio = 1 << 0, .polarity = REVERSE }, + }, + }, + [WRTSL54GS] = { + .name = "Linksys WRTSL54GS", + .buttons = { + { .name = "reset", .gpio = 1 << 6 }, + { .name = "ses", .gpio = 1 << 4 }, + }, + .leds = { + { .name = "power", .gpio = 1 << 1, .polarity = NORMAL }, + { .name = "dmz", .gpio = 1 << 7, .polarity = REVERSE }, + { .name = "ses_white", .gpio = 1 << 5, .polarity = REVERSE }, + { .name = "ses_orange", .gpio = 1 << 7, .polarity = REVERSE }, + }, + }, + [WRT54G3G] = { + .name = "Linksys WRT54G3G", + .buttons = { + { .name = "reset", .gpio = 1 << 6 }, + { .name = "3g", .gpio = 1 << 4 }, + }, + .leds = { + { .name = "power", .gpio = 1 << 1, .polarity = NORMAL }, + { .name = "dmz", .gpio = 1 << 7, .polarity = REVERSE }, + { .name = "3g_green", .gpio = 1 << 2, .polarity = NORMAL }, + { .name = "3g_blue", .gpio = 1 << 3, .polarity = NORMAL }, + { .name = "3g_blink", .gpio = 1 << 5, .polarity = NORMAL }, + }, + }, + /* Asus */ + [WLHDD] = { + .name = "ASUS WL-HDD", + .buttons = { + { .name = "reset", .gpio = 1 << 6 }, + }, + .leds = { + { .name = "power", .gpio = 1 << 0, .polarity = REVERSE }, + }, + }, + [WL300G] = { + .name = "ASUS WL-500g", + .buttons = { + { .name = "reset", .gpio = 1 << 6 }, + }, + .leds = { + { .name = "power", .gpio = 1 << 0, .polarity = REVERSE }, + }, + }, + [WL500G] = { + .name = "ASUS WL-500g", + .buttons = { + { .name = "reset", .gpio = 1 << 6 }, + }, + .leds = { + { .name = "power", .gpio = 1 << 0, .polarity = REVERSE }, + }, + }, + [WL500GD] = { + .name = "ASUS WL-500g Deluxe", + .buttons = { + { .name = "reset", .gpio = 1 << 6 }, + }, + .leds = { + { .name = "power", .gpio = 1 << 0, .polarity = REVERSE }, + }, + }, + [WL500GP] = { + .name = "ASUS WL-500g Premium", + .buttons = { + { .name = "reset", .gpio = 1 << 0 }, + { .name = "ses", .gpio = 1 << 4 }, + }, + .leds = { + { .name = "power", .gpio = 1 << 1, .polarity = REVERSE }, + }, + }, + [ASUS_4702] = { + .name = "ASUS (unknown, BCM4702)", + .buttons = { + { .name = "reset", .gpio = 1 << 6 }, + }, + .leds = { + { .name = "power", .gpio = 1 << 0, .polarity = REVERSE }, + }, + }, + /* Buffalo */ + [WHR_G54S] = { + .name = "Buffalo WHR-G54S", + .buttons = { + { .name = "reset", .gpio = 1 << 4 }, + { .name = "ses", .gpio = 1 << 0 }, + }, + .leds = { + { .name = "diag", .gpio = 1 << 1, .polarity = REVERSE }, + { .name = "internal", .gpio = 1 << 3, .polarity = REVERSE }, + { .name = "ses", .gpio = 1 << 6, .polarity = REVERSE }, + }, + }, + [WBR2_G54] = { + .name = "Buffalo WBR2-G54", + /* FIXME: verify */ + .buttons = { + { .name = "reset", .gpio = 1 << 7 }, + }, + .leds = { + { .name = "diag", .gpio = 1 << 1, .polarity = REVERSE }, + }, + }, + [WHR_HP_G54] = { + .name = "Buffalo WHR-HP-G54", + .buttons = { + { .name = "reset", .gpio = 1 << 4 }, + { .name = "bridge", .gpio = 1 << 5 }, + { .name = "ses", .gpio = 1 << 0 }, + }, + .leds = { + { .name = "diag", .gpio = 1 << 7, .polarity = REVERSE }, + { .name = "bridge", .gpio = 1 << 1, .polarity = REVERSE }, + { .name = "ses", .gpio = 1 << 6, .polarity = REVERSE }, + }, + }, + [WHR2_A54G54] = { + .name = "Buffalo WHR2-A54G54", + .buttons = { + { .name = "reset", .gpio = 1 << 4 }, + }, + .leds = { + { .name = "diag", .gpio = 1 << 7, .polarity = REVERSE }, + }, + }, + [WLA2_G54L] = { + .name = "Buffalo WLA2-G54L", + /* FIXME: verify */ + .buttons = { + { .name = "reset", .gpio = 1 << 7 }, + }, + .leds = { + { .name = "diag", .gpio = 1 << 1, .polarity = REVERSE }, + }, + }, + [BUFFALO_UNKNOWN] = { + .name = "Buffalo (unknown)", + .buttons = { + { .name = "reset", .gpio = 1 << 7 }, + }, + .leds = { + { .name = "diag", .gpio = 1 << 1, .polarity = REVERSE }, + }, + }, + [BUFFALO_UNKNOWN_4710] = { + .name = "Buffalo (unknown, BCM4710)", + .buttons = { + { .name = "reset", .gpio = 1 << 4 }, + }, + .leds = { + { .name = "diag", .gpio = 1 << 1, .polarity = REVERSE }, + }, + }, + /* Siemens */ + [SE505V1] = { + .name = "Siemens SE505 V1", + .buttons = { + /* No usable buttons */ + }, + .leds = { + { .name = "dmz", .gpio = 1 << 4, .polarity = REVERSE }, + { .name = "wlan", .gpio = 1 << 3, .polarity = REVERSE }, + }, + }, + [SE505V2] = { + .name = "Siemens SE505 V2", + .buttons = { + /* No usable buttons */ + }, + .leds = { + { .name = "power", .gpio = 1 << 5, .polarity = REVERSE }, + { .name = "dmz", .gpio = 1 << 0, .polarity = REVERSE }, + { .name = "wlan", .gpio = 1 << 3, .polarity = REVERSE }, + }, + }, + /* US Robotics */ + [USR5461] = { + .name = "U.S. Robotics USR5461", + .buttons = { + /* No usable buttons */ + }, + .leds = { + { .name = "wlan", .gpio = 1 << 0, .polarity = REVERSE }, + { .name = "printer", .gpio = 1 << 1, .polarity = REVERSE }, + }, + }, + /* Dell */ + [TM2300] = { + .name = "Dell TrueMobile 2300", + .buttons = { + { .name = "reset", .gpio = 1 << 0 }, + }, + .leds = { + { .name = "diag", .gpio = 1 << 7, .polarity = REVERSE }, + }, + }, + /* Motorola */ + [WE800G] = { + .name = "Motorola WE800G", + .buttons = { + { .name = "reset", .gpio = 1 << 0 }, + }, + .leds = { + { .name = "power", .gpio = 1 << 4, .polarity = NORMAL }, + { .name = "diag", .gpio = 1 << 2, .polarity = REVERSE }, + { .name = "wlan_amber", .gpio = 1 << 1, .polarity = NORMAL }, + }, + }, + [WR850GV1] = { + .name = "Motorola WR850G V1", + .buttons = { + { .name = "reset", .gpio = 1 << 0 }, + }, + .leds = { + { .name = "power", .gpio = 1 << 4, .polarity = NORMAL }, + { .name = "diag", .gpio = 1 << 3, .polarity = REVERSE }, + { .name = "modem", .gpio = 1 << 6, .polarity = NORMAL }, + { .name = "wlan_red", .gpio = 1 << 5, .polarity = REVERSE }, + { .name = "wlan_green", .gpio = 1 << 7, .polarity = REVERSE }, + }, + }, + [WR850GV2] = { + .name = "Motorola WR850G V2", + .buttons = { + { .name = "reset", .gpio = 1 << 5 }, + }, + .leds = { + { .name = "diag", .gpio = 1 << 1, .polarity = REVERSE }, + { .name = "wlan", .gpio = 1 << 0, .polarity = NORMAL }, + { .name = "modem_green",.gpio = 1 << 6, .polarity = REVERSE }, + { .name = "modem_red", .gpio = 1 << 7, .polarity = REVERSE }, + }, + }, + /* Belkin */ + [BELKIN_UNKNOWN] = { + .name = "Belkin (unknown)", + /* FIXME: verify & add detection */ + .buttons = { + { .name = "reset", .gpio = 1 << 7 }, + }, + .leds = { + { .name = "power", .gpio = 1 << 5, .polarity = NORMAL }, + { .name = "wlan", .gpio = 1 << 3, .polarity = NORMAL }, + { .name = "connected", .gpio = 1 << 0, .polarity = NORMAL }, + }, + }, +}; + +extern void *sbh; +extern spinlock_t sbh_lock; +extern char *nvram_get(char *str); + +static struct proc_dir_entry *diag, *leds; +static struct platform_t platform; +static void led_flash(unsigned long dummy); + +#define getvar(str) (nvram_get(str)?:"") + +static void set_led_extif(struct led_t *led) +{ + volatile u8 *addr = (volatile u8 *) KSEG1ADDR(EXTIF_UART) + (led->gpio & ~GPIO_TYPE_MASK); + if (led->state) + *addr = 0xFF; + else + *addr; +} + +static struct platform_t __init *platform_detect(void) +{ + char *boardnum, *boardtype, *buf; + + boardnum = getvar("boardnum"); + boardtype = getvar("boardtype"); + if (strncmp(getvar("pmon_ver"), "CFE", 3) == 0) { + /* CFE based - newer hardware */ + if (!strcmp(boardnum, "42")) { /* Linksys */ + if (!strcmp(boardtype, "0x0101")) + return &platforms[WRT54G3G]; + + if (!strcmp(getvar("et1phyaddr"),"5") && !strcmp(getvar("et1mdcport"), "1")) + return &platforms[WRTSL54GS]; + + /* default to WRT54G */ + return &platforms[WRT54G]; + } + + if (!strcmp(boardnum, "45")) { /* ASUS */ + if (!strcmp(boardtype,"0x042f")) + return &platforms[WL500GP]; + else + return &platforms[WL500GD]; + } + + if (!strcmp(boardnum, "10496")) + return &platforms[USR5461]; + } else { /* PMON based - old stuff */ + if (!strncmp(boardtype, "bcm94710dev", 11)) { + if (!strcmp(boardnum, "42")) + return &platforms[WRT54GV1]; + if (simple_strtoul(boardnum, NULL, 9) == 2) + return &platforms[WAP54GV1]; + } + if (!strncmp(getvar("hardware_version"), "WL500-", 6)) + return &platforms[WL500G]; + if (!strncmp(getvar("hardware_version"), "WL300-", 6)) { + /* Either WL-300g or WL-HDD, do more extensive checks */ + if ((simple_strtoul(getvar("et0phyaddr"), NULL, 0) == 0) && + (simple_strtoul(getvar("et1phyaddr"), NULL, 9) == 1)) + return &platforms[WLHDD]; + if ((simple_strtoul(getvar("et0phyaddr"), NULL, 0) == 0) && + (simple_strtoul(getvar("et1phyaddr"), NULL, 9) == 10)) + return &platforms[WL300G]; + } + + /* unknown asus stuff, probably bcm4702 */ + if (!strncmp(boardnum, "asusX", 5)) + return &platforms[ASUS_4702]; + + if ((simple_strtoul(getvar("GemtekPmonVer"), NULL, 0) == 9) && + (simple_strtoul(getvar("et0phyaddr"), NULL, 0) == 30)) { + if (!strncmp(getvar("ModelId"),"WE800G", 6)) + return &platforms[WE800G]; + else + return &platforms[WR850GV1]; + } + } + + if ((buf = (nvram_get("melco_id") ?: nvram_get("buffalo_id")))) { + /* Buffalo hardware, check id for specific hardware matches */ + if (!strcmp(buf, "29bb0332")) + return &platforms[WBR2_G54]; + if (!strcmp(buf, "29129")) + return &platforms[WLA2_G54L]; + if (!strcmp(buf, "30189")) + return &platforms[WHR_HP_G54]; + if (!strcmp(buf, "30182")) + return &platforms[WHR_G54S]; + if (!strcmp(buf, "290441dd")) + return &platforms[WHR2_A54G54]; + } + + if (buf || !strcmp(boardnum, "00")) {/* probably buffalo */ + if (!strncmp(boardtype, "bcm94710ap", 10)) + return &platforms[BUFFALO_UNKNOWN_4710]; + else + return &platforms[BUFFALO_UNKNOWN]; + } + + if (!strcmp(getvar("CFEver"), "MotoWRv203")) + return &platforms[WR850GV2]; + + /* not found */ + return NULL; +} + +static ssize_t diag_proc_read(struct file *file, char *buf, size_t count, loff_t *ppos); +static ssize_t diag_proc_write(struct file *file, const char *buf, size_t count, void *data); +static struct file_operations diag_proc_fops = { + read: diag_proc_read, + write: diag_proc_write +}; + + + +static ssize_t diag_proc_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ +#ifdef LINUX_2_4 + struct inode *inode = file->f_dentry->d_inode; + struct proc_dir_entry *dent = inode->u.generic_ip; +#else + struct proc_dir_entry *dent = PDE(file->f_dentry->d_inode); +#endif + char *page; + int len = 0; + + if ((page = kmalloc(1024, GFP_KERNEL)) == NULL) + return -ENOBUFS; + + if (dent->data != NULL) { + struct prochandler_t *handler = (struct prochandler_t *) dent->data; + switch (handler->type) { + case PROC_LED: { + struct led_t * led = (struct led_t *) handler->ptr; + if (led->flash) { + len = sprintf(page, "f\n"); + } else { + if (led->gpio & GPIO_TYPE_EXTIF) { + len = sprintf(page, "%d\n", led->state); + } else { + int in = (sb_gpioin(sbh) & led->gpio ? 1 : 0); + int p = (led->polarity == NORMAL ? 0 : 1); + len = sprintf(page, "%d\n", ((in ^ p) ? 1 : 0)); + } + } + break; + } + case PROC_MODEL: + len = sprintf(page, "%s\n", platform.name); + break; + case PROC_GPIOMASK: + len = sprintf(page, "%d\n", gpiomask); + break; + } + } + len += 1; + + if (*ppos < len) { + len = min_t(int, len - *ppos, count); + if (copy_to_user(buf, (page + *ppos), len)) { + kfree(page); + return -EFAULT; + } + *ppos += len; + } else { + len = 0; + } + + return len; +} + + +static ssize_t diag_proc_write(struct file *file, const char *buf, size_t count, void *data) +{ +#ifdef LINUX_2_4 + struct inode *inode = file->f_dentry->d_inode; + struct proc_dir_entry *dent = inode->u.generic_ip; +#else + struct proc_dir_entry *dent = PDE(file->f_dentry->d_inode); +#endif + char *page; + int ret = -EINVAL; + + if ((page = kmalloc(count + 1, GFP_KERNEL)) == NULL) + return -ENOBUFS; + + if (copy_from_user(page, buf, count)) { + kfree(page); + return -EINVAL; + } + page[count] = 0; + + if (dent->data != NULL) { + struct prochandler_t *handler = (struct prochandler_t *) dent->data; + switch (handler->type) { + case PROC_LED: { + struct led_t *led = (struct led_t *) handler->ptr; + int p = (led->polarity == NORMAL ? 0 : 1); + + if (!(led->gpio & GPIO_TYPE_EXTIF) && (led->gpio & gpiomask)) + break; + + if (page[0] == 'f') { + led->flash = 1; + led_flash(0); + } else { + led->flash = 0; + if (led->gpio & GPIO_TYPE_EXTIF) { + led->state = p ^ ((page[0] == '1') ? 1 : 0); + set_led_extif(led); + } else { + sb_gpioouten(sbh, led->gpio, led->gpio, GPIO_DRV_PRIORITY); + sb_gpiocontrol(sbh, led->gpio, 0, GPIO_DRV_PRIORITY); + sb_gpioout(sbh, led->gpio, ((p ^ (page[0] == '1')) ? led->gpio : 0), GPIO_DRV_PRIORITY); + } + } + break; + } + case PROC_GPIOMASK: + gpiomask = simple_strtoul(page, NULL, 16); + break; + } + ret = count; + } + + kfree(page); + return ret; +} + +struct event_t { + struct work_struct tq; + char buf[256]; + char *argv[3]; + char *envp[6]; +}; + +static void hotplug_button(struct event_t *event) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + call_usermodehelper (event->argv[0], event->argv, event->envp, 1); +#else + call_usermodehelper (event->argv[0], event->argv, event->envp); +#endif + kfree(event); +} + +static void set_irqenable(int enabled) +{ + unsigned int coreidx; + unsigned long flags; + chipcregs_t *cc; + + spin_lock_irqsave(sbh_lock, flags); + coreidx = sb_coreidx(sbh); + if ((cc = sb_setcore(sbh, SB_CC, 0))) { + int intmask; + + intmask = readl(&cc->intmask); + if (enabled) + intmask |= CI_GPIO; + else + intmask &= ~CI_GPIO; + writel(intmask, &cc->intmask); + } + sb_setcoreidx(sbh, coreidx); + spin_unlock_irqrestore(sbh_lock, flags); +} + + +static irqreturn_t button_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + struct button_t *b; + int in = sb_gpioin(sbh); + struct event_t *event; + + set_irqenable(0); + for (b = platform.buttons; b->name; b++) { + if (b->gpio & gpiomask) + continue; + + if (b->polarity != (in & b->gpio)) { + b->pressed ^= 1; + + if ((event = (struct event_t *)kmalloc (sizeof(struct event_t), GFP_ATOMIC))) { + int i; + char *scratch = event->buf; + + i = 0; + event->argv[i++] = hotplug_path; + event->argv[i++] = "button"; + event->argv[i] = 0; + + i = 0; + event->envp[i++] = "HOME=/"; + event->envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + event->envp[i++] = scratch; + scratch += sprintf (scratch, "ACTION=%s", b->pressed?"pressed":"released") + 1; + event->envp[i++] = scratch; + scratch += sprintf (scratch, "BUTTON=%s", b->name) + 1; + event->envp[i++] = scratch; + scratch += sprintf (scratch, "SEEN=%ld", (jiffies - b->seen)/HZ) + 1; + event->envp[i] = 0; + + INIT_WORK(&event->tq, (void *)(void *)hotplug_button, (void *)event); + schedule_work(&event->tq); + } + + b->seen = jiffies; + b->polarity ^= b->gpio; + sb_gpiointpolarity(sbh, b->gpio, b->polarity, GPIO_DRV_PRIORITY); + } + } + set_irqenable(1); + return IRQ_HANDLED; +} + +static struct timer_list led_timer = { + function: &led_flash +}; + +static void led_flash(unsigned long dummy) { + struct led_t *l; + unsigned mask = 0; + unsigned extif_blink = 0; + + for (l = platform.leds; l->name; l++) { + if (l->flash) { + if (l->gpio & GPIO_TYPE_EXTIF) { + extif_blink = 1; + l->state = !l->state; + set_led_extif(l); + } else { + mask |= l->gpio; + } + } + } + + mask &= ~gpiomask; + if (mask) { + unsigned val; + + val = ~sb_gpioin(sbh); + val &= mask; + + sb_gpioouten(sbh, mask, mask, GPIO_DRV_PRIORITY); + sb_gpiocontrol(sbh, mask, 0, GPIO_DRV_PRIORITY); + sb_gpioout(sbh, mask, val, GPIO_DRV_PRIORITY); + } + if (mask || extif_blink) { + mod_timer(&led_timer, jiffies + FLASH_TIME); + } +} + +static void __init register_buttons(struct button_t *b) +{ + int irq = sb_irq(sbh) + 2; + + request_irq(irq, button_handler, SA_SHIRQ | SA_SAMPLE_RANDOM, "gpio", button_handler); + + for (; b->name; b++) { + if (b->gpio & gpiomask) + continue; + + sb_gpioouten(sbh, b->gpio,0, GPIO_DRV_PRIORITY); + sb_gpiocontrol(sbh, b->gpio,0, GPIO_DRV_PRIORITY); + b->polarity = sb_gpioin(sbh) & b->gpio; + sb_gpiointpolarity(sbh, b->gpio, b->polarity, GPIO_DRV_PRIORITY); + sb_gpiointmask(sbh, b->gpio, b->gpio, GPIO_DRV_PRIORITY); + } + set_irqenable(1); +} + +static void __exit unregister_buttons(struct button_t *b) +{ + int irq = sb_irq(sbh) + 2; + + for (; b->name; b++) + sb_gpiointmask(sbh, b->gpio, 0, GPIO_DRV_PRIORITY); + + free_irq(irq, button_handler); +} + +static void __init register_leds(struct led_t *l) +{ + struct proc_dir_entry *p; + + leds = proc_mkdir("led", diag); + if (!leds) + return; + + for(; l->name; l++) { + if (l->gpio & gpiomask) + continue; + + if (l->gpio & GPIO_TYPE_EXTIF) { + l->state = 0; + set_led_extif(l); + } else { + sb_gpioouten(sbh, l->gpio, l->gpio, GPIO_DRV_PRIORITY); + sb_gpiocontrol(sbh, l->gpio, 0, GPIO_DRV_PRIORITY); + sb_gpioout(sbh, l->gpio, (l->polarity == NORMAL)?0:l->gpio, GPIO_DRV_PRIORITY); + } + + if ((p = create_proc_entry(l->name, S_IRUSR, leds))) { + l->proc.type = PROC_LED; + l->proc.ptr = l; + p->data = (void *) &l->proc; + p->proc_fops = &diag_proc_fops; + } + } +} + +static void __exit unregister_leds(struct led_t *l) +{ + for(; l->name; l++) + remove_proc_entry(l->name, leds); + + remove_proc_entry("led", diag); +} + +static void __exit diag_exit(void) +{ + + del_timer(&led_timer); + + if (platform.buttons) + unregister_buttons(platform.buttons); + + if (platform.leds) + unregister_leds(platform.leds); + + remove_proc_entry("model", diag); + remove_proc_entry("gpiomask", diag); + remove_proc_entry("diag", NULL); +} + +static struct prochandler_t proc_model = { .type = PROC_MODEL }; +static struct prochandler_t proc_gpiomask = { .type = PROC_GPIOMASK }; + +static int __init diag_init(void) +{ + static struct proc_dir_entry *p; + static struct platform_t *detected; + + detected = platform_detect(); + if (!detected) { + printk(MODULE_NAME ": Router model not detected.\n"); + return -ENODEV; + } + memcpy(&platform, detected, sizeof(struct platform_t)); + + printk(MODULE_NAME ": Detected '%s'\n", platform.name); + + if (!(diag = proc_mkdir("diag", NULL))) { + printk(MODULE_NAME ": proc_mkdir on /proc/diag failed\n"); + return -EINVAL; + } + + if ((p = create_proc_entry("model", S_IRUSR, diag))) { + p->data = (void *) &proc_model; + p->proc_fops = &diag_proc_fops; + } + + if ((p = create_proc_entry("gpiomask", S_IRUSR | S_IWUSR, diag))) { + p->data = (void *) &proc_gpiomask; + p->proc_fops = &diag_proc_fops; + } + + if (platform.buttons) + register_buttons(platform.buttons); + + if (platform.leds) + register_leds(platform.leds); + + return 0; +} + +module_init(diag_init); +module_exit(diag_exit); + +MODULE_AUTHOR("Mike Baker, Felix Fietkau / OpenWrt.org"); +MODULE_LICENSE("GPL"); diff --git a/package/diag/src/diag.c b/package/diag/src/diag.c deleted file mode 100644 index d25f749df5..0000000000 --- a/package/diag/src/diag.c +++ /dev/null @@ -1,284 +0,0 @@ -/* - * diag_led.c - replacement diag module - * - * Copyright (C) 2004 Mike Baker, - * Imre Kaloz <kaloz@dune.hu> - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * $Id$ - */ - -/* - * ChangeLog: - * 2004/03/28 initial release - * 2004/08/26 asus & buffalo support added - * 2005/03/14 asus wl-500g deluxe and buffalo v2 support added - * 2005/04/13 added licensing informations - * 2005/04/18 base reset polarity off initial readings - * 2006/02/07 motorola wa840g/we800g support added - * 2006/08/18 asus power led support added - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/sysctl.h> -#include <asm/io.h> -#include <typedefs.h> -#include <osl.h> -#include <bcmdevs.h> -#include <sbutils.h> - -extern char * nvram_get(const char *name); -static void *sbh; - -// v2.x - - - - - -#define DIAG_GPIO (1<<1) -#define DMZ_GPIO (1<<7) - -static void set_gpio(uint32 mask, uint32 value) { - sb_gpiocontrol(sbh,mask, 0, GPIO_DRV_PRIORITY); - sb_gpioouten(sbh,mask,mask,GPIO_DRV_PRIORITY); - sb_gpioout(sbh,mask,value,GPIO_DRV_PRIORITY); -} - -static void v2_set_diag(u8 state) { - set_gpio(DIAG_GPIO,state); -} -static void v2_set_dmz(u8 state) { - set_gpio(DMZ_GPIO,state); -} - -// asus wl-500g (+deluxe) -#define ASUS_PWR_GPIO (1<<0) - -static void asus_set_pwr(u8 state) { - set_gpio(ASUS_PWR_GPIO,state); -} - -// v1.x - - - - - -#define LED_DIAG 0x13 -#define LED_DMZ 0x12 - -static void v1_set_diag(u8 state) { - if (!state) { - *(volatile u8*)(KSEG1ADDR(BCM4710_EUART)+LED_DIAG)=0xFF; - } else { - *(volatile u8*)(KSEG1ADDR(BCM4710_EUART)+LED_DIAG); - } -} -static void v1_set_dmz(u8 state) { - if (!state) { - *(volatile u8*)(KSEG1ADDR(BCM4710_EUART)+LED_DMZ)=0xFF; - } else { - *(volatile u8*)(KSEG1ADDR(BCM4710_EUART)+LED_DMZ); - } -} - -static void wap1_set_diag(u8 state) { - set_gpio(1<<3,state); -} -static void wap1_set_dmz(u8 state) { - set_gpio(1<<4,state); -} - -// - - - - - -static void ignore(u8 ignored) {}; - -// - - - - - -#define BIT_DMZ 0x01 -#define BIT_PWR 0x02 -#define BIT_DIAG 0x04 - -void (*set_diag)(u8 state); -void (*set_dmz)(u8 state); -void (*set_pwr)(u8 state); - -static unsigned int diag = 0x02; // default: diag off, pwr on, dmz off - -static void diag_change(void) -{ - set_diag(0xFF); // off - set_dmz(0xFF); // off - set_pwr(0xFF); // off - - if(diag & BIT_DIAG) - set_diag(0x00); // on - if(diag & BIT_DMZ) - set_dmz(0x00); // on - if (diag & BIT_PWR) - set_pwr(0x00); //on -} - -static int proc_diag(ctl_table *table, int write, struct file *filp, - void *buffer, size_t *lenp) -{ - int r; - r = proc_dointvec(table, write, filp, buffer, lenp); - if (write && !r) { - diag_change(); - } - return r; -} - -// - - - - - -static unsigned char reset_gpio = 0; -static unsigned char reset_polarity = 0; -static unsigned int reset = 0; - -static int proc_reset(ctl_table *table, int write, struct file *filp, - void *buffer, size_t *lenp) -{ - - if (reset_gpio) { - sb_gpiocontrol(sbh,reset_gpio,reset_gpio,GPIO_DRV_PRIORITY); - sb_gpioouten(sbh,reset_gpio,0,GPIO_DRV_PRIORITY); - reset=!(sb_gpioin(sbh)&reset_gpio); - - if (reset_polarity) reset=!reset; - } else { - reset=0; - } - - return proc_dointvec(table, write, filp, buffer, lenp); -} - -// - - - - - -static struct ctl_table_header *diag_sysctl_header; - -static ctl_table sys_diag[] = { - { - ctl_name: 2000, - procname: "diag", - data: &diag, - maxlen: sizeof(diag), - mode: 0644, - proc_handler: proc_diag - }, - { - ctl_name: 2001, - procname: "reset", - data: &reset, - maxlen: sizeof(reset), - mode: 0444, - proc_handler: proc_reset - }, - { 0 } -}; - -static int __init diag_init(void) -{ - char *buf; - u32 board_type; - sbh = (void *)sb_kattach(); - sb_gpiosetcore(sbh); - - board_type = sb_boardtype(sbh); - printk(KERN_INFO "diag boardtype: %08x\n",board_type); - - set_diag=ignore; - set_dmz=ignore; - set_pwr=ignore; - - buf=nvram_get("pmon_ver") ?: ""; - if (((board_type & 0xf00) == 0x400) && (strncmp(buf, "CFE", 3) != 0)) { - buf=nvram_get("boardtype")?:""; - if (!strncmp(buf,"bcm94710dev",11)) { - buf=nvram_get("boardnum")?:""; - if (!strcmp(buf,"42")) { - // wrt54g v1.x - set_diag=v1_set_diag; - set_dmz=v1_set_dmz; - reset_gpio=(1<<6); - } - if (simple_strtoul(buf, NULL, 0) == 2) { - // wap54g v1.0 - // do not use strcmp as PMON v5.3.22 has some built-in nvram - // defaults with trailing \r - set_diag=wap1_set_diag; - // no dmz led on wap54g, used green led - // labeled "WLAN Link" instead - set_dmz=wap1_set_dmz; - reset_gpio=(1<<0); - } - if (!strcmp(buf,"asusX")) { - //asus wl-500g - set_pwr=asus_set_pwr; - reset_gpio=(1<<6); - } - if (!strcmp(buf,"2")) { - //wa840g v1 / we800g v1 - reset_gpio=(1<<0); - } - } - if (!strcmp(buf,"bcm94710ap")) { - buf=nvram_get("boardnum")?:""; - if (!strcmp(buf,"42")) { - // buffalo - set_dmz=v2_set_dmz; - reset_gpio=(1<<4); - } - if (!strcmp(buf,"44")) { - //dell truemobile - set_dmz=v2_set_dmz; - reset_gpio=(1<<0); - } - } - } else { - buf=nvram_get("boardnum")?:""; - if (!strcmp(buf,"42")) { - //linksys - set_diag=v2_set_diag; - set_dmz=v2_set_dmz; - reset_gpio=(1<<6); - } - if (!strcmp(buf,"44")) { - //motorola - reset_gpio=(1<<5); - } - if (!strcmp(buf,"00")) { - //buffalo - reset_gpio=(1<<7); - } - if (!strcmp(buf,"45")) { - //wl-500g deluxe - set_pwr=asus_set_pwr; - reset_gpio=(1<<6); - } - } - - - sb_gpiocontrol(sbh,reset_gpio,reset_gpio,GPIO_DRV_PRIORITY); - sb_gpioouten(sbh,reset_gpio,0,GPIO_DRV_PRIORITY); - reset_polarity=!(sb_gpioin(sbh)&reset_gpio); - - diag_sysctl_header = register_sysctl_table(sys_diag, 0); - diag_change(); - - return 0; -} - -static void __exit diag_exit(void) -{ - unregister_sysctl_table(diag_sysctl_header); -} - -EXPORT_NO_SYMBOLS; -MODULE_AUTHOR("openwrt.org"); -MODULE_LICENSE("GPL"); - -module_init(diag_init); -module_exit(diag_exit); |