blob: 70b9b33f3522fd21fe803f11c5b0db24a074a9e9 [file] [log] [blame]
/* 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 - 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 ***/