/* -*- linux-c -*- */ /* * Copyright (C) 2001 By Joachim Martillo, Telford Tools, 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. * **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Reg9050.h" #include "8253xctl.h" #include "ring.h" #include "8253x.h" #include "crc32dcl.h" /* turns network packet into a pseudoethernet */ /* frame -- does ethernet stuff that 8253x does */ /* not do -- makes minimum 64 bytes add crc, etc*/ int sab8253xn_write2(struct sk_buff *skb, struct net_device *dev) { size_t cnt; unsigned int flags; SAB_PORT *priv = (SAB_PORT*) dev->priv; struct sk_buff *substitute; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) if(dev->tbusy != 0) /* something of an error */ { ++(priv->Counters.tx_drops); dev_kfree_skb_any(skb); return -EBUSY; /* only during release */ } #endif if(priv->active2.transmit == NULL) { return -ENOMEM; } DEBUGPRINT((KERN_ALERT "sab8253x: sending IP packet(bytes):\n")); DEBUGPRINT((KERN_ALERT "sab8253x: start address is %p.\n", skb->data)); cnt = skb->tail - skb->data; cnt = MIN(cnt, sab8253xn_rbufsize); if(cnt < ETH_ZLEN) { if((skb->end - skb->data) >= ETH_ZLEN) { skb->tail = (skb->data + ETH_ZLEN); cnt = ETH_ZLEN; } else { substitute = dev_alloc_skb(ETH_ZLEN); if(substitute == NULL) { dev_kfree_skb_any(skb); return 0; } substitute->tail = (substitute->data + ETH_ZLEN); memcpy(substitute->data, skb->data, cnt); cnt = ETH_ZLEN; dev_kfree_skb_any(skb); skb = substitute; } } save_flags(flags); cli(); if((priv->active2.transmit->Count & OWNER) == OWN_SAB) { ++(priv->Counters.tx_drops); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) dev->tbusy = 1; #else netif_stop_queue (dev); #endif priv->tx_full = 1; restore_flags(flags); return 1; } restore_flags(flags); #ifndef FREEINTERRUPT if(priv->active2.transmit->HostVaddr != NULL) { register RING_DESCRIPTOR *freeme; freeme = priv->active2.transmit; do { skb_unlink((struct sk_buff*)freeme->HostVaddr); dev_kfree_skb_any((struct sk_buff*)freeme->HostVaddr); freeme->HostVaddr = NULL; freeme = (RING_DESCRIPTOR*) freeme->VNext; } while(((freeme->Count & OWNER) != OWN_SAB) && (freeme->HostVaddr != NULL)); } #endif dev->trans_start = jiffies; skb_queue_head(priv->sab8253xbuflist, skb); priv->active2.transmit->HostVaddr = skb; priv->active2.transmit->sendcrc = 1; priv->active2.transmit->crcindex = 0; priv->active2.transmit->crc = fn_calc_memory_crc32(skb->data, cnt); priv->active2.transmit->Count = (OWN_SAB|cnt); /* must be this order */ priv->active2.transmit = (RING_DESCRIPTOR*) priv->active2.transmit->VNext; priv->Counters.transmitbytes += cnt; sab8253x_start_txS(priv); return 0; } /* packetizes the received character */ /* stream */ static void sab8253x_receive_charsN(struct sab_port *port, union sab8253x_irq_status *stat) { unsigned char buf[32]; int free_fifo = 0; int reset_fifo = 0; int msg_done = 0; int msg_bad = 0; int count = 0; int total_size = 0; int rstatus = 0; struct sk_buff *skb; /* Read number of BYTES (Character + Status) available. */ if((stat->images[ISR1_IDX] & SAB82532_ISR1_RDO) || (stat->images[ISR0_IDX] & SAB82532_ISR0_RFO) ) { ++msg_bad; ++free_fifo; ++reset_fifo; } else { if (stat->images[ISR0_IDX] & SAB82532_ISR0_RPF) { count = port->recv_fifo_size; ++free_fifo; } if (stat->images[ISR0_IDX] & SAB82532_ISR0_RME) { count = READB(port,rbcl); count &= (port->recv_fifo_size - 1); ++msg_done; ++free_fifo; total_size = READB(port, rbch); if(total_size & SAB82532_RBCH_OV) { msg_bad++; } rstatus = READB(port, rsta); if((rstatus & SAB82532_RSTA_VFR) == 0) { msg_bad++; } if(rstatus & SAB82532_RSTA_RDO) { msg_bad++; } if((rstatus & SAB82532_RSTA_CRC) == 0) { msg_bad++; } if(rstatus & SAB82532_RSTA_RAB) { msg_bad++; } } } /* Read the FIFO. */ (*port->readfifo)(port, buf, count); /* Issue Receive Message Complete command. */ if (free_fifo) { sab8253x_cec_wait(port); WRITEB(port, cmdr, SAB82532_CMDR_RMC); } if(reset_fifo) { sab8253x_cec_wait(port); WRITEB(port, cmdr, SAB82532_CMDR_RHR); } if(port->active2.receive == NULL) { return; } if(msg_bad) { ++(port->Counters.rx_drops); port->active2.receive->HostVaddr->tail = port->active2.receive->HostVaddr->data; /* clear the buffer */ port->active2.receive->Count = sab8253xn_rbufsize|OWN_SAB; return; } memcpy(port->active2.receive->HostVaddr->tail, buf, count); port->active2.receive->HostVaddr->tail += count; if(msg_done) { port->active2.receive->Count = (port->active2.receive->HostVaddr->tail - port->active2.receive->HostVaddr->data); if((port->active2.receive->Count < (ETH_ZLEN+4+3)) || /* 4 is the CRC32 size 3 bytes from the SAB part */ (skb = dev_alloc_skb(sab8253xn_rbufsize), skb == NULL)) { ++(port->Counters.rx_drops); port->active2.receive->HostVaddr->tail = port->active2.receive->HostVaddr->data; /* clear the buffer */ port->active2.receive->Count = sab8253xn_rbufsize|OWN_SAB; } else { port->active2.receive->Count -= 3; port->active2.receive->HostVaddr->len = port->active2.receive->Count; port->active2.receive->HostVaddr->pkt_type = PACKET_HOST; port->active2.receive->HostVaddr->dev = port->dev; port->active2.receive->HostVaddr->protocol = eth_type_trans(port->active2.receive->HostVaddr, port->dev); port->active2.receive->HostVaddr->tail -= 3; ++(port->Counters.receivepacket); port->Counters.receivebytes += port->active2.receive->Count; skb_unlink(port->active2.receive->HostVaddr); netif_rx(port->active2.receive->HostVaddr); skb_queue_head(port->sab8253xbuflist, skb); port->active2.receive->HostVaddr = skb; port->active2.receive->Count = sab8253xn_rbufsize|OWN_SAB; } } } static void sab8253x_check_statusN(struct sab_port *port, union sab8253x_irq_status *stat) { int modem_change = 0; mctlsig_t *sig; if (stat->images[ISR0_IDX] & SAB82532_ISR0_RFO) { port->icount.buf_overrun++; } /* Checking DCD */ sig = &port->dcd; if (stat->images[sig->irq] & sig->irqmask) { sig->val = ISON(port,dcd); port->icount.dcd++; modem_change++; } /* Checking CTS */ sig = &port->cts; if (stat->images[sig->irq] & sig->irqmask) { sig->val = ISON(port,cts); port->icount.cts++; modem_change++; } /* Checking DSR */ sig = &port->dsr; if (stat->images[sig->irq] & sig->irqmask) { sig->val = ISON(port,dsr); port->icount.dsr++; modem_change++; } if (modem_change) { wake_up_interruptible(&port->delta_msr_wait); } sig = &port->dcd; if ((port->flags & FLAG8253X_CHECK_CD) && (stat->images[sig->irq] & sig->irqmask)) { if (sig->val) { netif_carrier_on(port->dev); } else if (!((port->flags & FLAG8253X_CALLOUT_ACTIVE) && (port->flags & FLAG8253X_CALLOUT_NOHUP))) { netif_carrier_off(port->dev); } } #if 0 /* need to think about CTS/RTS stuff for a network driver */ sig = &port->cts; if (port->flags & FLAG8253X_CTS_FLOW) { /* not setting this yet */ if (port->tty->hw_stopped) { if (sig->val) { port->tty->hw_stopped = 0; sab8253x_sched_event(port, RS_EVENT_WRITE_WAKEUP); port->interrupt_mask1 &= ~(SAB82532_IMR1_XPR); WRITEB(port, imr1, port->interrupt_mask1); sab8253x_start_txS(port); } } else { if (!(sig->val)) { port->tty->hw_stopped = 1; } } } #endif } static void Sab8253xCollectStats(struct net_device *dev) { struct net_device_stats *statsp = &((SAB_PORT*) dev->priv)->stats; memset(statsp, 0, sizeof(struct net_device_stats)); statsp->rx_packets += ((SAB_PORT*)dev->priv)->Counters.receivepacket; statsp->tx_packets += ((SAB_PORT*)dev->priv)->Counters.transmitpacket; statsp->tx_dropped += ((SAB_PORT*)dev->priv)->Counters.tx_drops; statsp->rx_dropped += ((SAB_PORT*)dev->priv)->Counters.rx_drops; } struct net_device_stats *sab8253xn_stats(struct net_device *dev) { SAB_PORT *priv = (SAB_PORT*) dev->priv; Sab8253xCollectStats(dev); return &priv->stats; } /* minimal ioctls -- more to be added later */ int sab8253xn_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { SAB_PORT *priv = (SAB_PORT*) dev->priv; switch(cmd) { case SAB8253XCLEARCOUNTERS: memset(&priv->Counters, 0, sizeof(struct counters)); break; default: break; } return 0; } #if 0 static int sab8253x_block_til_readyN(SAB_PORT *port) { DECLARE_WAITQUEUE(wait, current); int retval; int do_clocal = 0; unsigned long flags; /* * If the device is in the middle of being closed, then block * until it's done, and then try again. */ if (port->flags & FLAG8253X_CLOSING) { if (port->flags & FLAG8253X_CLOSING) { interruptible_sleep_on(&port->close_wait); } #ifdef SERIAL_DO_RESTART if (port->flags & FLAG8253X_HUP_NOTIFY) { return -EAGAIN; } else { return -ERESTARTSYS; } #else return -EAGAIN; #endif } /* * this is not a callout device */ /* suppose callout active */ if (port->flags & FLAG8253X_CALLOUT_ACTIVE) { if (port->normal_termios.c_cflag & CLOCAL) { do_clocal = 1; } } /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in * this loop, port->count is dropped by one, so that * sab8253x_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; add_wait_queue(&port->open_wait, &wait); port->blocked_open++; while (1) { save_flags(flags); cli(); if (!(port->flags & FLAG8253X_CALLOUT_ACTIVE)) { RAISE(port,dtr); RAISE(port,rts); /* maybe not correct for sync */ /* * ??? Why changing the mode here? * port->regs->rw.mode |= SAB82532_MODE_FRTS; * port->regs->rw.mode &= ~(SAB82532_MODE_RTS); */ } restore_flags(flags); current->state = TASK_INTERRUPTIBLE; if (!(port->flags & FLAG8253X_INITIALIZED)) { #ifdef SERIAL_DO_RESTART if (port->flags & FLAG8253X_HUP_NOTIFY) { retval = -EAGAIN; } else { retval = -ERESTARTSYS; } #else retval = -EAGAIN; #endif break; } if (!(port->flags & FLAG8253X_CALLOUT_ACTIVE) && !(port->flags & FLAG8253X_CLOSING) && (do_clocal || ISON(port,dcd))) { break; } #ifdef DEBUG_OPEN printk("block_til_readyN:2 flags = 0x%x\n",port->flags); #endif if (signal_pending(current)) { retval = -ERESTARTSYS; break; } schedule(); } current->state = TASK_RUNNING; remove_wait_queue(&port->open_wait, &wait); port->blocked_open--; if (retval) { return retval; } port->flags |= FLAG8253X_NORMAL_ACTIVE; /* is this a good flag? */ return 0; } #endif int sab8253x_startupN(struct sab_port *port) { unsigned long flags; int retval = 0; save_flags(flags); cli(); if (port->flags & FLAG8253X_INITIALIZED) { goto errout; } if (!port->regs) { retval = -ENODEV; goto errout; } /* * Initialize the Hardware */ sab8253x_init_lineS(port); /* nothing in this function * refers to tty structure */ /* Activate RTS */ RAISE(port,rts); /* Activate DTR */ RAISE(port,dtr); /* * Initialize the modem signals values */ port->dcd.val=ISON(port,dcd); port->cts.val=ISON(port,cts); port->dsr.val=ISON(port,dsr); /* * Finally, enable interrupts */ port->interrupt_mask0 = SAB82532_IMR0_RFS | SAB82532_IMR0_PCE | SAB82532_IMR0_PLLA | SAB82532_IMR0_RSC | SAB82532_IMR0_CDSC; /*((port->ccontrol.ccr2 & SAB82532_CCR2_TOE) ? SAB82532_IMR0_CDSC : 0); */ WRITEB(port,imr0,port->interrupt_mask0); port->interrupt_mask1 = SAB82532_IMR1_EOP | SAB82532_IMR1_XMR | SAB82532_IMR1_TIN | SAB82532_IMR1_XPR; WRITEB(port, imr1, port->interrupt_mask1); port->all_sent = 1; /* * and set the speed of the serial port */ sab8253x_change_speedN(port); port->flags |= FLAG8253X_INITIALIZED; /* bad name for indicating to other functionalities status */ port->receive_chars = sab8253x_receive_charsN; port->transmit_chars = sab8253x_transmit_charsS; port->check_status = sab8253x_check_statusN; port->receive_test = (SAB82532_ISR0_RME | SAB82532_ISR0_RFO | SAB82532_ISR0_RPF); port->transmit_test = (SAB82532_ISR1_ALLS | SAB82532_ISR1_RDO | SAB82532_ISR1_XPR | SAB82532_ISR1_XDU | SAB82532_ISR1_CSC); port->check_status_test = (SAB82532_ISR1_CSC); /*((port->ccontrol.ccr2 & SAB82532_CCR2_TOE) ? 0 : SAB82532_ISR0_CDSC));*/ restore_flags(flags); return 0; errout: restore_flags(flags); return retval; } int sab8253xn_open(struct net_device *dev) { unsigned int retval; SAB_PORT *priv = (SAB_PORT*) dev->priv; if(priv->function != FUNCTION_NR) { return -ENODEV; /* only allowed if there are no restrictions on the port */ } if(priv->flags & FLAG8253X_CLOSING) /* try again after the TTY close finishes */ { #ifdef SERIAL_DO_RESTART return ((priv->flags & FLAG8253X_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); /* The ifconfig UP will just fail */ #else return -EAGAIN; #endif } /* * Maybe start up serial port -- may already be running a TTY */ if(priv->flags & FLAG8253X_NORMAL_ACTIVE) /* probably should be a test open at all */ { return -EBUSY; /* can't reopen in NET */ } if(Sab8253xSetUpLists(priv)) { return -ENODEV; } if(Sab8253xInitDescriptors2(priv, sab8253xn_listsize, sab8253xn_rbufsize)) { Sab8253xCleanUpTransceiveN(priv); return -ENODEV; } netif_carrier_off(dev); priv->open_type = OPEN_SYNC_NET; priv->tty = 0; retval = sab8253x_startupN(priv); if (retval) { Sab8253xCleanUpTransceiveN(priv); return retval; } priv->flags |= FLAG8253X_NETWORK; /* flag the call out driver that it has to reinitialize the port */ priv->tx_full = 0; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) dev->start = 1; dev->tbusy = 0; #else netif_start_queue(dev); #endif priv->flags |= FLAG8253X_NORMAL_ACTIVE; /* is this a good flag? */ MOD_INC_USE_COUNT; return 0; /* success */ } /* stop the PPC, free all skbuffers */ int sab8253xn_release(struct net_device *dev) /* stop */ { SAB_PORT *priv = (SAB_PORT*) dev->priv; unsigned long flags; printk(KERN_ALERT "sab8253xn: network interface going down.\n"); save_flags(flags); cli(); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) dev->start = 0; dev->tbusy = 1; #else netif_stop_queue (dev); #endif sab8253x_shutdownN(priv); Sab8253xCleanUpTransceiveN(priv); netif_carrier_off(dev); priv->flags &= ~FLAG8253X_NETWORK; priv->flags &= ~(FLAG8253X_NORMAL_ACTIVE|/*FLAG8253X_CALLOUT_ACTIVE|*/ FLAG8253X_CLOSING); priv->open_type = OPEN_NOT; MOD_DEC_USE_COUNT; restore_flags(flags); return 0; } SAB_PORT *current_sab_port = NULL; int sab8253xn_init(struct net_device *dev) { SAB_PORT *priv; printk(KERN_ALERT "sab8253xn: initializing SAB8253X network driver instance.\n"); priv = current_sab_port; dev->priv = priv; if(dev->priv == NULL) { printk(KERN_ALERT "sab8253xn: could not find active port!\n"); return -ENOMEM; } priv->dev = dev; ether_setup(dev); dev->irq = priv->irq; dev->hard_start_xmit = sab8253xn_write2; dev->do_ioctl = sab8253xn_ioctl; dev->open = sab8253xn_open; dev->stop = sab8253xn_release; dev->get_stats = sab8253xn_stats; dev->base_addr = (unsigned) priv->regs; /* should I do a request region here */ priv->next_dev = Sab8253xRoot; Sab8253xRoot = dev; return 0; }