/* 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. Load more
/* 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_NCOMM irqreturn_t c4_ebus_intr_th_handler (void *); #endif int 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_t mkret (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. */ void c4_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_t c4_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_t c4_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 */ } void c4_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_t c4_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_NCOMM irqreturn_t c4_ebus_interrupt (int irq, void *dev_instance) { struct net_device *ndev = dev_instance; return c4_ebus_intr_th_handler(netdev_priv(ndev)); } #endif static int void_open (struct net_device *ndev) { pr_info("%s: trying to open master device !\n", ndev->name); return -1; } static int chan_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 int chan_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 int chan_dev_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd) { return hdlc_ioctl (dev, ifr, cmd); } static int chan_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 int c4_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 */ }