1184 lines · 33.1 KB · C
RawDownload
/* Copyright (C) 2007-2008  One Stop Systems * Copyright (C) 2003-2006  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/netdevice.h>#include <linux/module.h>#include <linux/hdlc.h>#include <linux/if_arp.h>#include <linux/init.h>#include <asm/uaccess.h>#include <linux/rtnetlink.h>#include <linux/skbuff.h>#include "pmcc4_sysdep.h"#include "sbecom_inline_linux.h"#include "libsbew.h"#include "pmcc4.h"#include "pmcc4_ioctls.h"#include "pmcc4_private.h"#include "sbeproc.h" /***************************************************************************************** * Error out early if we have compiler trouble. * *   (This section is included from the kernel's init/main.c as a friendly *   spiderman recommendation...) * * Versions of gcc older than that listed below may actually compile and link * okay, but the end product can have subtle run time bugs.  To avoid associated * bogus bug reports, we flatly refuse to compile with a gcc that is known to be * too old from the very beginning. */#if (__GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 2)#error Sorry, your GCC is too old. It builds incorrect kernels.#endif #if __GNUC__ == 4 && __GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ == 0#warning gcc-4.1.0 is known to miscompile the kernel.  A different compiler version is recommended.#endif /*****************************************************************************************/ #define CHANNAME "hdlc" /*******************************************************************//* forward references */status_t    c4_chan_work_init (mpi_t *, mch_t *);void        musycc_wq_chan_restart (void *);status_t __init c4_init (ci_t *, u_char *, u_char *);status_t __init c4_init2 (ci_t *);ci_t       *__init c4_new (void *);int __init  c4hw_attach_all (void);void __init hdw_sn_get (hdw_info_t *, int); #ifdef CONFIG_SBE_PMCC4_NCOMMirqreturn_t c4_ebus_intr_th_handler (void *); #endifint         c4_frame_rw (ci_t *, struct sbecom_port_param *);status_t    c4_get_port (ci_t *, int);int         c4_loop_port (ci_t *, int, u_int8_t);int         c4_musycc_rw (ci_t *, struct c4_musycc_param *);int         c4_new_chan (ci_t *, int, int, void *);status_t    c4_set_port (ci_t *, int);int         c4_pld_rw (ci_t *, struct sbecom_port_param *);void        cleanup_devs (void);void        cleanup_ioremap (void);status_t    musycc_chan_down (ci_t *, int);irqreturn_t musycc_intr_th_handler (void *);int         musycc_start_xmit (ci_t *, int, void *); extern char pmcc4_OSSI_release[];extern ci_t *CI;extern struct s_hdw_info hdw_info[]; #if defined(CONFIG_SBE_HDLC_V7) || defined(CONFIG_SBE_WAN256T3_HDLC_V7) || \    defined(CONFIG_SBE_HDLC_V7_MODULE) || defined(CONFIG_SBE_WAN256T3_HDLC_V7_MODULE)#define _v7_hdlc_  1#else#define _v7_hdlc_  0#endif #if _v7_hdlc_#define V7(x) (x ## _v7)extern int  hdlc_netif_rx_v7 (hdlc_device *, struct sk_buff *);extern int  register_hdlc_device_v7 (hdlc_device *);extern int  unregister_hdlc_device_v7 (hdlc_device *); #else#define V7(x) x#endif int         error_flag;         /* module load error reporting */int         cxt1e1_log_level = LOG_ERROR;int         log_level_default = LOG_ERROR;module_param(cxt1e1_log_level, int, 0444); int         cxt1e1_max_mru = MUSYCC_MRU;int         max_mru_default = MUSYCC_MRU;module_param(cxt1e1_max_mru, int, 0444); int         cxt1e1_max_mtu = MUSYCC_MTU;int         max_mtu_default = MUSYCC_MTU;module_param(cxt1e1_max_mtu, int, 0444); int         max_txdesc_used = MUSYCC_TXDESC_MIN;int         max_txdesc_default = MUSYCC_TXDESC_MIN;module_param(max_txdesc_used, int, 0444); int         max_rxdesc_used = MUSYCC_RXDESC_MIN;int         max_rxdesc_default = MUSYCC_RXDESC_MIN;module_param(max_rxdesc_used, int, 0444); /****************************************************************************//****************************************************************************//****************************************************************************/ void       *getuserbychan (int channum){    mch_t      *ch;     ch = c4_find_chan (channum);    return ch ? ch->user : NULL;}  char       *get_hdlc_name (hdlc_device *hdlc){    struct c4_priv *priv = hdlc->priv;    struct net_device *dev = getuserbychan (priv->channum);     return dev->name;}  static      status_tmkret (int bsd){    if (bsd > 0)        return -bsd;    else        return bsd;} /***************************************************************************/#include <linux/workqueue.h> /*** * One workqueue (wq) per port (since musycc allows simultaneous group * commands), with individual data for each channel: * *   mpi_t -> struct workqueue_struct *wq_port;  (dynamically allocated using *                                               create_workqueue()) * * With work structure (work) statically allocated for each channel: * *   mch_t -> struct work_struct ch_work;  (statically allocated using ???) * ***/  /* * Called by the start transmit routine when a channel TX_ENABLE is to be * issued.  This queues the transmission start request among other channels * within a port's group. */voidc4_wk_chan_restart (mch_t *ch){    mpi_t      *pi = ch->up; #ifdef RLD_RESTART_DEBUG    pr_info(">> %s: queueing Port %d Chan %d, mch_t @ %p\n",            __func__, pi->portnum, ch->channum, ch);#endif     /* create new entry w/in workqueue for this channel and let'er rip */     /** queue_work (struct workqueue_struct *queue,     **             struct work_struct *work);     **/    queue_work (pi->wq_port, &ch->ch_work);} status_tc4_wk_chan_init (mpi_t *pi, mch_t *ch){    /*     * this will be used to restart a stopped channel     */     /** INIT_WORK (struct work_struct *work,     **            void (*function)(void *),     **            void *data);     **/    INIT_WORK(&ch->ch_work, (void *)musycc_wq_chan_restart);    return 0;                       /* success */} status_tc4_wq_port_init (mpi_t *pi){     char        name[16], *np;  /* NOTE: name of the queue limited by system                                 * to 10 characters */     if (pi->wq_port)        return 0;                   /* already initialized */     np = name;    memset (name, 0, 16);    sprintf (np, "%s%d", pi->up->devname, pi->portnum); /* IE pmcc4-01) */ #ifdef RLD_RESTART_DEBUG    pr_info(">> %s: creating workqueue <%s> for Port %d.\n",            __func__, name, pi->portnum); /* RLD DEBUG */#endif    if (!(pi->wq_port = create_singlethread_workqueue (name)))        return -ENOMEM;    return 0;                       /* success */} voidc4_wq_port_cleanup (mpi_t *pi){    /*     * PORT POINT: cannot call this if WQ is statically allocated w/in     * structure since it calls kfree(wq);     */    if (pi->wq_port)    {        destroy_workqueue (pi->wq_port);        /* this also calls                                                 * flush_workqueue() */        pi->wq_port = NULL;    }} /***************************************************************************/ irqreturn_tc4_linux_interrupt (int irq, void *dev_instance){    struct net_device *ndev = dev_instance;     return musycc_intr_th_handler(netdev_priv(ndev));}  #ifdef CONFIG_SBE_PMCC4_NCOMMirqreturn_tc4_ebus_interrupt (int irq, void *dev_instance){    struct net_device *ndev = dev_instance;     return c4_ebus_intr_th_handler(netdev_priv(ndev));}#endif  static intvoid_open (struct net_device *ndev){    pr_info("%s: trying to open master device !\n", ndev->name);    return -1;}  static intchan_open (struct net_device *ndev){    hdlc_device *hdlc = dev_to_hdlc (ndev);    const struct c4_priv *priv = hdlc->priv;    int         ret;     if ((ret = hdlc_open (ndev)))    {        pr_info("hdlc_open failure, err %d.\n", ret);        return ret;    }    if ((ret = c4_chan_up (priv->ci, priv->channum)))        return -ret;    try_module_get (THIS_MODULE);    netif_start_queue (ndev);    return 0;                       /* no error = success */}  static intchan_close (struct net_device *ndev){    hdlc_device *hdlc = dev_to_hdlc (ndev);    const struct c4_priv *priv = hdlc->priv;     netif_stop_queue (ndev);    musycc_chan_down ((ci_t *) 0, priv->channum);    hdlc_close (ndev);    module_put (THIS_MODULE);    return 0;}  static intchan_dev_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd){    return hdlc_ioctl (dev, ifr, cmd);}  static intchan_attach_noop (struct net_device *ndev, unsigned short foo_1, unsigned short foo_2){    return 0;                   /* our driver has nothing to do here, show's                                 * over, go home */}  static struct net_device_stats *chan_get_stats (struct net_device *ndev){    mch_t      *ch;    struct net_device_stats *nstats;    struct sbecom_chan_stats *stats;    int         channum;     {        struct c4_priv *priv;         priv = (struct c4_priv *) dev_to_hdlc (ndev)->priv;        channum = priv->channum;    }     ch = c4_find_chan (channum);    if (ch == NULL)        return NULL;     nstats = &ndev->stats;    stats = &ch->s;     memset (nstats, 0, sizeof (struct net_device_stats));    nstats->rx_packets = stats->rx_packets;    nstats->tx_packets = stats->tx_packets;    nstats->rx_bytes = stats->rx_bytes;    nstats->tx_bytes = stats->tx_bytes;    nstats->rx_errors = stats->rx_length_errors +        stats->rx_over_errors +        stats->rx_crc_errors +        stats->rx_frame_errors +        stats->rx_fifo_errors +        stats->rx_missed_errors;    nstats->tx_errors = stats->tx_dropped +        stats->tx_aborted_errors +        stats->tx_fifo_errors;    nstats->rx_dropped = stats->rx_dropped;    nstats->tx_dropped = stats->tx_dropped;     nstats->rx_length_errors = stats->rx_length_errors;    nstats->rx_over_errors = stats->rx_over_errors;    nstats->rx_crc_errors = stats->rx_crc_errors;    nstats->rx_frame_errors = stats->rx_frame_errors;    nstats->rx_fifo_errors = stats->rx_fifo_errors;    nstats->rx_missed_errors = stats->rx_missed_errors;     nstats->tx_aborted_errors = stats->tx_aborted_errors;    nstats->tx_fifo_errors = stats->tx_fifo_errors;     return nstats;}  static ci_t *get_ci_by_dev (struct net_device *ndev){    return (ci_t *)(netdev_priv(ndev));}  static intc4_linux_xmit (struct sk_buff *skb, struct net_device *ndev){    const struct c4_priv *priv;    int         rval;     hdlc_device *hdlc = dev_to_hdlc (ndev);     priv = hdlc->priv;     rval = musycc_start_xmit (priv->ci, priv->channum, skb);    return rval;} static const struct net_device_ops chan_ops = {       .ndo_open       = chan_open,       .ndo_stop       = chan_close,       .ndo_start_xmit = c4_linux_xmit,       .ndo_do_ioctl   = chan_dev_ioctl,       .ndo_get_stats  = chan_get_stats,}; static struct net_device *create_chan (struct net_device *ndev, ci_t *ci,             struct sbecom_chan_param *cp){    hdlc_device *hdlc;    struct net_device *dev;    hdw_info_t *hi;    int         ret;     if (c4_find_chan (cp->channum))        return NULL;                   /* channel already exists */     {        struct c4_priv *priv;         /* allocate then fill in private data structure */        priv = OS_kmalloc (sizeof (struct c4_priv));        if (!priv)        {            pr_warning("%s: no memory for net_device !\n", ci->devname);	    return NULL;        }        dev = alloc_hdlcdev (priv);        if (!dev)        {            pr_warning("%s: no memory for hdlc_device !\n", ci->devname);            OS_kfree (priv);	    return NULL;        }        priv->ci = ci;        priv->channum = cp->channum;    }     hdlc = dev_to_hdlc (dev);     dev->base_addr = 0;             /* not I/O mapped */    dev->irq = ndev->irq;    dev->type = ARPHRD_RAWHDLC;    *dev->name = 0;                 /* default ifconfig name = "hdlc" */     hi = (hdw_info_t *) ci->hdw_info;    if (hi->mfg_info_sts == EEPROM_OK)    {        switch (hi->promfmt)        {        case PROM_FORMAT_TYPE1:            memcpy (dev->dev_addr, (FLD_TYPE1 *) (hi->mfg_info.pft1.Serial), 6);            break;        case PROM_FORMAT_TYPE2:            memcpy (dev->dev_addr, (FLD_TYPE2 *) (hi->mfg_info.pft2.Serial), 6);            break;        default:            memset (dev->dev_addr, 0, 6);            break;        }    } else    {        memset (dev->dev_addr, 0, 6);    }     hdlc->xmit = c4_linux_xmit;     dev->netdev_ops = &chan_ops;    /*     * The native hdlc stack calls this 'attach' routine during     * hdlc_raw_ioctl(), passing parameters for line encoding and parity.     * Since hdlc_raw_ioctl() stack does not interrogate whether an 'attach'     * routine is actually registered or not, we supply a dummy routine which     * does nothing (since encoding and parity are setup for our driver via a     * special configuration application).     */     hdlc->attach = chan_attach_noop;     rtnl_unlock ();                 /* needed due to Ioctl calling sequence */    ret = register_hdlc_device (dev);    /* NOTE: <stats> setting must occur AFTER registration in order to "take" */    dev->tx_queue_len = MAX_DEFAULT_IFQLEN;     rtnl_lock ();                   /* needed due to Ioctl calling sequence */    if (ret)    {        if (cxt1e1_log_level >= LOG_WARN)            pr_info("%s: create_chan[%d] registration error = %d.\n",                    ci->devname, cp->channum, ret);        free_netdev (dev);          /* cleanup */	return NULL;		/* failed to register */    }

Lines 1–500 of 1184.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t