void cleanup_devs (void) { hdw_info_t *hi; int i; for (i = 0, hi = hdw_info; i < MAX_BOARDS; i++, hi++) { if (hi->pci_slot == 0xff || !hi->ndev) break; c4_stopwd(netdev_priv(hi->ndev)); #ifdef CONFIG_PROC_FS sbecom_proc_brd_cleanup(netdev_priv(hi->ndev)); #endif unregister_netdev (hi->ndev); free_irq (hi->pdev[0]->irq, hi->ndev); #ifdef CONFIG_SBE_PMCC4_NCOMM free_irq (hi->pdev[1]->irq, hi->ndev); #endif OS_kfree (hi->ndev); } }
struct net_device *__init c4_add_dev (hdw_info_t * hi, int brdno, unsigned long f0, unsigned long f1, int irq0, int irq1) { struct net_device *ndev; ci_t *ci; ndev = alloc_netdev(sizeof(ci_t), SBE_IFACETMPL, c4_setup); if (!ndev) { pr_warning("%s: no memory for struct net_device !\n", hi->devname); error_flag = ENOMEM; return 0; } ci = (ci_t *)(netdev_priv(ndev)); ndev->irq = irq0; ci->hdw_info = hi; ci->state = C_INIT; /* mark as hardware not available */ ci->next = c4_list; c4_list = ci; ci->brdno = ci->next ? ci->next->brdno + 1 : 0; if (CI == 0) CI = ci; /* DEBUG, only board 0 usage */ strcpy (ci->devname, hi->devname); ci->release = &pmcc4_OSSI_release[0]; /* tasklet */ #if defined(SBE_ISR_TASKLET) tasklet_init (&ci->ci_musycc_isr_tasklet, (void (*) (unsigned long)) musycc_intr_bh_tasklet, (unsigned long) ci); if (atomic_read (&ci->ci_musycc_isr_tasklet.count) == 0) tasklet_disable_nosync (&ci->ci_musycc_isr_tasklet); #elif defined(SBE_ISR_IMMEDIATE) ci->ci_musycc_isr_tq.routine = (void *) (unsigned long) musycc_intr_bh_tasklet; ci->ci_musycc_isr_tq.data = ci; #endif if (register_netdev (ndev) || (c4_init (ci, (u_char *) f0, (u_char *) f1) != SBE_DRVR_SUCCESS)) { OS_kfree (netdev_priv(ndev)); OS_kfree (ndev); error_flag = ENODEV; return 0; } /************************************************************* * int request_irq(unsigned int irq, * void (*handler)(int, void *, struct pt_regs *), * unsigned long flags, const char *dev_name, void *dev_id); * wherein: * irq -> The interrupt number that is being requested. * handler -> Pointer to handling function being installed. * flags -> A bit mask of options related to interrupt management. * dev_name -> String used in /proc/interrupts to show owner of interrupt. * dev_id -> Pointer (for shared interrupt lines) to point to its own * private data area (to identify which device is interrupting). * * extern void free_irq(unsigned int irq, void *dev_id); **************************************************************/ if (request_irq (irq0, &c4_linux_interrupt, IRQF_SHARED, ndev->name, ndev)) { pr_warning("%s: MUSYCC could not get irq: %d\n", ndev->name, irq0); unregister_netdev (ndev); OS_kfree (netdev_priv(ndev)); OS_kfree (ndev); error_flag = EIO; return 0; } #ifdef CONFIG_SBE_PMCC4_NCOMM if (request_irq (irq1, &c4_ebus_interrupt, IRQF_SHARED, ndev->name, ndev)) { pr_warning("%s: EBUS could not get irq: %d\n", hi->devname, irq1); unregister_netdev (ndev); free_irq (irq0, ndev); OS_kfree (netdev_priv(ndev)); OS_kfree (ndev); error_flag = EIO; return 0; } #endif /* setup board identification information */ { u_int32_t tmp; hdw_sn_get (hi, brdno); /* also sets PROM format type (promfmt) * for later usage */ switch (hi->promfmt) { case PROM_FORMAT_TYPE1: memcpy (ndev->dev_addr, (FLD_TYPE1 *) (hi->mfg_info.pft1.Serial), 6); memcpy (&tmp, (FLD_TYPE1 *) (hi->mfg_info.pft1.Id), 4); /* unaligned data * acquisition */ ci->brd_id = cpu_to_be32 (tmp); break; case PROM_FORMAT_TYPE2: memcpy (ndev->dev_addr, (FLD_TYPE2 *) (hi->mfg_info.pft2.Serial), 6); memcpy (&tmp, (FLD_TYPE2 *) (hi->mfg_info.pft2.Id), 4); /* unaligned data * acquisition */ ci->brd_id = cpu_to_be32 (tmp); break; default: ci->brd_id = 0; memset (ndev->dev_addr, 0, 6); break; } #if 1 sbeid_set_hdwbid (ci); /* requires bid to be preset */ #else sbeid_set_bdtype (ci); /* requires hdw_bid to be preset */ #endif } #ifdef CONFIG_PROC_FS sbecom_proc_brd_init (ci); #endif #if defined(SBE_ISR_TASKLET) tasklet_enable (&ci->ci_musycc_isr_tasklet); #endif if ((error_flag = c4_init2 (ci)) != SBE_DRVR_SUCCESS) { #ifdef CONFIG_PROC_FS sbecom_proc_brd_cleanup (ci); #endif unregister_netdev (ndev); free_irq (irq1, ndev); free_irq (irq0, ndev); OS_kfree (netdev_priv(ndev)); OS_kfree (ndev); return 0; /* failure, error_flag is set */ } return ndev; }
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 0; /* 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 0; } dev = alloc_hdlcdev (priv); if (!dev) { pr_warning("%s: no memory for hdlc_device !\n", ci->devname); OS_kfree (priv); return 0; } 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 0; /* failed to register */ } return dev; }