| /* 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: %02X-%02X-%02X-%02X-%02X-%02X\n", |
| bip->brd_mac_addr[0], bip->brd_mac_addr[1], bip->brd_mac_addr[2], |
| bip->brd_mac_addr[3], bip->brd_mac_addr[4], bip->brd_mac_addr[5]); |
| 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 max_mru; |
| #if 0 |
| extern int max_chans_used; |
| extern int max_mtu; |
| #endif |
| extern int max_rxdesc_used, max_txdesc_used; |
| |
| len += sprintf (buffer + len, "\nmax_mru: %d\n", max_mru); |
| #if 0 |
| len += sprintf (buffer + len, "\nmax_chans_used: %d\n", max_chans_used); |
| len += sprintf (buffer + len, "max_mtu: %d\n", 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 - intepretation 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 ***/ |