/* Copyright (C) 2004-2005 SBE, Inc. * * 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. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/types.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/proc_fs.h> #include <linux/sched.h> #include <asm/uaccess.h> #include "pmcc4_sysdep.h" #include "sbecom_inline_linux.h" #include "pmcc4_private.h" #include "sbeproc.h" /* forwards */ void sbecom_get_brdinfo (ci_t *, struct sbe_brd_info *, u_int8_t *); extern struct s_hdw_info hdw_info[MAX_BOARDS]; #ifdef CONFIG_PROC_FS /********************************************************************/ /* procfs stuff */ /********************************************************************/ void sbecom_proc_brd_cleanup (ci_t * ci) { if (ci->dir_dev) { char dir[7 + SBE_IFACETMPL_SIZE + 1]; snprintf(dir, sizeof(dir), "driver/%s", ci->devname); remove_proc_entry("info", ci->dir_dev); remove_proc_entry(dir, NULL); ci->dir_dev = NULL; } } static int sbecom_proc_get_sbe_info (char *buffer, char **start, off_t offset, int length, int *eof, void *priv) { ci_t *ci = (ci_t *) priv; int len = 0; char *spd; struct sbe_brd_info *bip; if (!(bip = OS_kmalloc (sizeof (struct sbe_brd_info)))) { return -ENOMEM; } #if 0 /** RLD DEBUG **/ pr_info(">> sbecom_proc_get_sbe_info: entered, offset %d. length %d.\n", (int) offset, (int) length); #endif { hdw_info_t *hi = &hdw_info[ci->brdno]; u_int8_t *bsn = 0; switch (hi->promfmt) { case PROM_FORMAT_TYPE1: bsn = (u_int8_t *) hi->mfg_info.pft1.Serial; break; case PROM_FORMAT_TYPE2: bsn = (u_int8_t *) hi->mfg_info.pft2.Serial; break; } sbecom_get_brdinfo (ci, bip, bsn); } #if 0 /** RLD DEBUG **/ pr_info(">> sbecom_get_brdinfo: returned, first_if %p <%s> last_if %p <%s>\n", (char *) &bip->first_iname, (char *) &bip->first_iname, (char *) &bip->last_iname, (char *) &bip->last_iname); #endif len += sprintf (buffer + len, "Board Type: "); switch (bip->brd_id) { case SBE_BOARD_ID (PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_WANPMC_C1T3): len += sprintf (buffer + len, "wanPMC-C1T3"); break; case SBE_BOARD_ID (PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_WANPTMC_256T3_E1): len += sprintf (buffer + len, "wanPTMC-256T3 <E1>"); break; case SBE_BOARD_ID (PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_WANPTMC_256T3_T1): len += sprintf (buffer + len, "wanPTMC-256T3 <T1>"); break; case SBE_BOARD_ID (PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_WANPTMC_C24TE1): len += sprintf (buffer + len, "wanPTMC-C24TE1"); break; case SBE_BOARD_ID (PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_WANPMC_C4T1E1): case SBE_BOARD_ID (PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_WANPMC_C4T1E1_L): len += sprintf (buffer + len, "wanPMC-C4T1E1"); break; case SBE_BOARD_ID (PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_WANPMC_C2T1E1): case SBE_BOARD_ID (PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_WANPMC_C2T1E1_L): len += sprintf (buffer + len, "wanPMC-C2T1E1"); break; case SBE_BOARD_ID (PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_WANPMC_C1T1E1): case SBE_BOARD_ID (PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_WANPMC_C1T1E1_L): len += sprintf (buffer + len, "wanPMC-C1T1E1"); break; case SBE_BOARD_ID (PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_WANPCI_C4T1E1): len += sprintf (buffer + len, "wanPCI-C4T1E1"); break; case SBE_BOARD_ID (PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_WANPCI_C2T1E1): len += sprintf (buffer + len, "wanPCI-C2T1E1"); break; case SBE_BOARD_ID (PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_WANPCI_C1T1E1): len += sprintf (buffer + len, "wanPCI-C1T1E1"); break; default: len += sprintf (buffer + len, "unknown"); break; } len += sprintf (buffer + len, " [%08X]\n", bip->brd_id); len += sprintf (buffer + len, "Board Number: %d\n", bip->brdno); len += sprintf (buffer + len, "Hardware ID: 0x%02X\n", ci->hdw_bid); len += sprintf (buffer + len, "Board SN: %06X\n", bip->brd_sn); len += sprintf(buffer + len, "Board MAC: %pMF\n", bip->brd_mac_addr); len += sprintf (buffer + len, "Ports: %d\n", ci->max_port); len += sprintf (buffer + len, "Channels: %d\n", bip->brd_chan_cnt); #if 1 len += sprintf (buffer + len, "Interface: %s -> %s\n", (char *) &bip->first_iname, (char *) &bip->last_iname); #else len += sprintf (buffer + len, "Interface: <not available> 1st %p lst %p\n", (char *) &bip->first_iname, (char *) &bip->last_iname); #endif switch (bip->brd_pci_speed) { case BINFO_PCI_SPEED_33: spd = "33Mhz"; break; case BINFO_PCI_SPEED_66: spd = "66Mhz"; break; default: spd = "<not available>"; break; } len += sprintf (buffer + len, "PCI Bus Speed: %s\n", spd); len += sprintf (buffer + len, "Release: %s\n", ci->release); #ifdef SBE_PMCC4_ENABLE { extern int cxt1e1_max_mru; #if 0 extern int max_chans_used; extern int cxt1e1_max_mtu; #endif extern int max_rxdesc_used, max_txdesc_used; len += sprintf (buffer + len, "\ncxt1e1_max_mru: %d\n", cxt1e1_max_mru); #if 0 len += sprintf (buffer + len, "\nmax_chans_used: %d\n", max_chans_used); len += sprintf (buffer + len, "cxt1e1_max_mtu: %d\n", cxt1e1_max_mtu); #endif len += sprintf (buffer + len, "max_rxdesc_used: %d\n", max_rxdesc_used); len += sprintf (buffer + len, "max_txdesc_used: %d\n", max_txdesc_used); } #endif OS_kfree (bip); /* cleanup */ /*** * How to be a proc read function * ------------------------------ * Prototype: * int f(char *buffer, char **start, off_t offset, * int count, int *peof, void *dat) * * Assume that the buffer is "count" bytes in size. * * If you know you have supplied all the data you * have, set *peof. * * You have three ways to return data: * 0) Leave *start = NULL. (This is the default.) * Put the data of the requested offset at that * offset within the buffer. Return the number (n) * of bytes there are from the beginning of the * buffer up to the last byte of data. If the * number of supplied bytes (= n - offset) is * greater than zero and you didn't signal eof * and the reader is prepared to take more data * you will be called again with the requested * offset advanced by the number of bytes * absorbed. This interface is useful for files * no larger than the buffer. * 1) Set *start = an unsigned long value less than * the buffer address but greater than zero. * Put the data of the requested offset at the * beginning of the buffer. Return the number of * bytes of data placed there. If this number is * greater than zero and you didn't signal eof * and the reader is prepared to take more data * you will be called again with the requested * offset advanced by *start. This interface is * useful when you have a large file consisting * of a series of blocks which you want to count * and return as wholes. * (Hack by Paul.Russell@rustcorp.com.au) * 2) Set *start = an address within the buffer. * Put the data of the requested offset at *start. * Return the number of bytes of data placed there. * If this number is greater than zero and you * didn't signal eof and the reader is prepared to * take more data you will be called again with the * requested offset advanced by the number of bytes * absorbed. */ #if 1 /* #4 - interpretation of above = set EOF, return len */ *eof = 1; #endif #if 0 /* * #1 - from net/wireless/atmel.c RLD NOTE -there's something wrong with * this plagarized code which results in this routine being called TWICE. * The second call returns ZERO, resulting in hidden failure, but at * least only a single message set is being displayed. */ if (len <= offset + length) *eof = 1; *start = buffer + offset; len -= offset; if (len > length) len = length; if (len < 0) len = 0; #endif #if 0 /* #2 from net/tokenring/olympic.c + * lanstreamer.c */ { off_t begin = 0; int size = 0; off_t pos = 0; size = len; pos = begin + size; if (pos < offset) { len = 0; begin = pos; } *start = buffer + (offset - begin); /* Start of wanted data */ len -= (offset - begin); /* Start slop */ if (len > length) len = length; /* Ending slop */ } #endif #if 0 /* #3 from * char/ftape/lowlevel/ftape-proc.c */ len = strlen (buffer); *start = NULL; if (offset + length >= len) *eof = 1; else *eof = 0; #endif #if 0 pr_info(">> proc_fs: returned len = %d., start %p\n", len, start); /* RLD DEBUG */ #endif /*** using NONE: returns = 314.314.314. using #1 : returns = 314, 0. using #2 : returns = 314, 0, 0. using #3 : returns = 314, 314. using #4 : returns = 314, 314. ***/ return len; } /* initialize the /proc subsystem for the specific SBE driver */ int __init sbecom_proc_brd_init (ci_t * ci) { struct proc_dir_entry *e; char dir[7 + SBE_IFACETMPL_SIZE + 1]; /* create a directory in the root procfs */ snprintf(dir, sizeof(dir), "driver/%s", ci->devname); ci->dir_dev = proc_mkdir(dir, NULL); if (!ci->dir_dev) { pr_err("Unable to create directory /proc/driver/%s\n", ci->devname); goto fail; } e = create_proc_read_entry ("info", S_IFREG | S_IRUGO, ci->dir_dev, sbecom_proc_get_sbe_info, ci); if (!e) { pr_err("Unable to create entry /proc/driver/%s/info\n", ci->devname); goto fail; } return 0; fail: sbecom_proc_brd_cleanup (ci); return 1; } #else /*** ! CONFIG_PROC_FS ***/ /* stubbed off dummy routines */ void sbecom_proc_brd_cleanup (ci_t * ci) { } int __init sbecom_proc_brd_init (ci_t * ci) { return 0; } #endif /*** CONFIG_PROC_FS ***/ /*** End-of-File ***/