📄 cosa.c
字号:
} /* Update the name of the region now we know the type of card */ release_region(base, is_8bit(cosa)?2:4); if (!request_region(base, is_8bit(cosa)?2:4, cosa->type)) { printk(KERN_DEBUG "cosa: changing name at 0x%x failed.\n", base); return -1; } /* Now do IRQ autoprobe */ if (irq < 0) { unsigned long irqs;/* printk(KERN_INFO "IRQ autoprobe\n"); */ irqs = probe_irq_on(); /* * Enable interrupt on tx buffer empty (it sure is) * really sure ? * FIXME: When this code is not used as module, we should * probably call udelay() instead of the interruptible sleep. */ current->state = TASK_INTERRUPTIBLE; cosa_putstatus(cosa, SR_TX_INT_ENA); schedule_timeout(30); irq = probe_irq_off(irqs); /* Disable all IRQs from the card */ cosa_putstatus(cosa, 0); /* Empty the received data register */ cosa_getdata8(cosa); if (irq < 0) { printk (KERN_INFO "cosa IRQ autoprobe: multiple interrupts obtained (%d, board at 0x%x)\n", irq, cosa->datareg); err = -1; goto err_out; } if (irq == 0) { printk (KERN_INFO "cosa IRQ autoprobe: no interrupt obtained (board at 0x%x)\n", cosa->datareg); /* return -1; */ } } cosa->irq = irq; cosa->num = nr_cards; cosa->usage = 0; cosa->nchannels = 2; /* FIXME: how to determine this? */ if (request_irq(cosa->irq, cosa_interrupt, 0, cosa->type, cosa)) { err = -1; goto err_out; } if (request_dma(cosa->dma, cosa->type)) { err = -1; goto err_out1; } cosa->bouncebuf = kmalloc(COSA_MTU, GFP_KERNEL|GFP_DMA); if (!cosa->bouncebuf) { err = -ENOMEM; goto err_out2; } sprintf(cosa->name, "cosa%d", cosa->num); /* Initialize the per-channel data */ cosa->chan = kmalloc(sizeof(struct channel_data)*cosa->nchannels, GFP_KERNEL); if (!cosa->chan) { err = -ENOMEM; goto err_out3; } memset(cosa->chan, 0, sizeof(struct channel_data)*cosa->nchannels); for (i=0; i<cosa->nchannels; i++) { cosa->chan[i].cosa = cosa; cosa->chan[i].num = i; channel_init(cosa->chan+i); } printk (KERN_INFO "cosa%d: %s (%s at 0x%x irq %d dma %d), %d channels\n", cosa->num, cosa->id_string, cosa->type, cosa->datareg, cosa->irq, cosa->dma, cosa->nchannels); return nr_cards++;err_out3: kfree(cosa->bouncebuf);err_out2: free_dma(cosa->dma);err_out1: free_irq(cosa->irq, cosa);err_out: release_region(cosa->datareg,is_8bit(cosa)?2:4); printk(KERN_NOTICE "cosa%d: allocating resources failed\n", cosa->num); return err;}/*---------- SPPP/HDLC netdevice ---------- */static void cosa_setup(struct net_device *d){ d->open = cosa_sppp_open; d->stop = cosa_sppp_close; d->hard_start_xmit = cosa_sppp_tx; d->do_ioctl = cosa_sppp_ioctl; d->get_stats = cosa_net_stats; d->tx_timeout = cosa_sppp_timeout; d->watchdog_timeo = TX_TIMEOUT;}static void sppp_channel_init(struct channel_data *chan){ struct net_device *d; chan->if_ptr = &chan->pppdev; d = alloc_netdev(0, chan->name, cosa_setup); if (!d) { printk(KERN_WARNING "%s: alloc_netdev failed.\n", chan->name); return; } chan->pppdev.dev = d; sppp_attach(&chan->pppdev); d->base_addr = chan->cosa->datareg; d->irq = chan->cosa->irq; d->dma = chan->cosa->dma; d->priv = chan; if (register_netdev(d)) { printk(KERN_WARNING "%s: register_netdev failed.\n", d->name); sppp_detach(d); free_netdev(d); chan->pppdev.dev = NULL; return; }}static void sppp_channel_delete(struct channel_data *chan){ unregister_netdev(chan->pppdev.dev); sppp_detach(chan->pppdev.dev); free_netdev(chan->pppdev.dev); chan->pppdev.dev = NULL;}static int cosa_sppp_open(struct net_device *d){ struct channel_data *chan = d->priv; int err; unsigned long flags; if (!(chan->cosa->firmware_status & COSA_FW_START)) { printk(KERN_NOTICE "%s: start the firmware first (status %d)\n", chan->cosa->name, chan->cosa->firmware_status); return -EPERM; } spin_lock_irqsave(&chan->cosa->lock, flags); if (chan->usage != 0) { printk(KERN_WARNING "%s: sppp_open called with usage count %d\n", chan->name, chan->usage); spin_unlock_irqrestore(&chan->cosa->lock, flags); return -EBUSY; } chan->setup_rx = sppp_setup_rx; chan->tx_done = sppp_tx_done; chan->rx_done = sppp_rx_done; chan->usage=-1; chan->cosa->usage++; spin_unlock_irqrestore(&chan->cosa->lock, flags); err = sppp_open(d); if (err) { spin_lock_irqsave(&chan->cosa->lock, flags); chan->usage=0; chan->cosa->usage--; spin_unlock_irqrestore(&chan->cosa->lock, flags); return err; } netif_start_queue(d); cosa_enable_rx(chan); return 0;}static int cosa_sppp_tx(struct sk_buff *skb, struct net_device *dev){ struct channel_data *chan = dev->priv; netif_stop_queue(dev); chan->tx_skb = skb; cosa_start_tx(chan, skb->data, skb->len); return 0;}static void cosa_sppp_timeout(struct net_device *dev){ struct channel_data *chan = dev->priv; if (test_bit(RXBIT, &chan->cosa->rxtx)) { chan->stats.rx_errors++; chan->stats.rx_missed_errors++; } else { chan->stats.tx_errors++; chan->stats.tx_aborted_errors++; } cosa_kick(chan->cosa); if (chan->tx_skb) { dev_kfree_skb(chan->tx_skb); chan->tx_skb = NULL; } netif_wake_queue(dev);}static int cosa_sppp_close(struct net_device *d){ struct channel_data *chan = d->priv; unsigned long flags; netif_stop_queue(d); sppp_close(d); cosa_disable_rx(chan); spin_lock_irqsave(&chan->cosa->lock, flags); if (chan->rx_skb) { kfree_skb(chan->rx_skb); chan->rx_skb = NULL; } if (chan->tx_skb) { kfree_skb(chan->tx_skb); chan->tx_skb = NULL; } chan->usage=0; chan->cosa->usage--; spin_unlock_irqrestore(&chan->cosa->lock, flags); return 0;}static char *sppp_setup_rx(struct channel_data *chan, int size){ /* * We can safely fall back to non-dma-able memory, because we have * the cosa->bouncebuf pre-allocated. */ if (chan->rx_skb) kfree_skb(chan->rx_skb); chan->rx_skb = dev_alloc_skb(size); if (chan->rx_skb == NULL) { printk(KERN_NOTICE "%s: Memory squeeze, dropping packet\n", chan->name); chan->stats.rx_dropped++; return NULL; } chan->pppdev.dev->trans_start = jiffies; return skb_put(chan->rx_skb, size);}static int sppp_rx_done(struct channel_data *chan){ if (!chan->rx_skb) { printk(KERN_WARNING "%s: rx_done with empty skb!\n", chan->name); chan->stats.rx_errors++; chan->stats.rx_frame_errors++; return 0; } chan->rx_skb->protocol = htons(ETH_P_WAN_PPP); chan->rx_skb->dev = chan->pppdev.dev; chan->rx_skb->mac.raw = chan->rx_skb->data; chan->stats.rx_packets++; chan->stats.rx_bytes += chan->cosa->rxsize; netif_rx(chan->rx_skb); chan->rx_skb = NULL; chan->pppdev.dev->last_rx = jiffies; return 0;}/* ARGSUSED */static int sppp_tx_done(struct channel_data *chan, int size){ if (!chan->tx_skb) { printk(KERN_WARNING "%s: tx_done with empty skb!\n", chan->name); chan->stats.tx_errors++; chan->stats.tx_aborted_errors++; return 1; } dev_kfree_skb_irq(chan->tx_skb); chan->tx_skb = NULL; chan->stats.tx_packets++; chan->stats.tx_bytes += size; netif_wake_queue(chan->pppdev.dev); return 1;}static struct net_device_stats *cosa_net_stats(struct net_device *dev){ struct channel_data *chan = dev->priv; return &chan->stats;}/*---------- Character device ---------- */static void chardev_channel_init(struct channel_data *chan){ init_MUTEX(&chan->rsem); init_MUTEX(&chan->wsem);}static ssize_t cosa_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){ DECLARE_WAITQUEUE(wait, current); unsigned long flags; struct channel_data *chan = file->private_data; struct cosa_data *cosa = chan->cosa; char *kbuf; if (!(cosa->firmware_status & COSA_FW_START)) { printk(KERN_NOTICE "%s: start the firmware first (status %d)\n", cosa->name, cosa->firmware_status); return -EPERM; } if (down_interruptible(&chan->rsem)) return -ERESTARTSYS; if ((chan->rxdata = kmalloc(COSA_MTU, GFP_DMA|GFP_KERNEL)) == NULL) { printk(KERN_INFO "%s: cosa_read() - OOM\n", cosa->name); up(&chan->rsem); return -ENOMEM; } chan->rx_status = 0; cosa_enable_rx(chan); spin_lock_irqsave(&cosa->lock, flags); add_wait_queue(&chan->rxwaitq, &wait); while(!chan->rx_status) { current->state = TASK_INTERRUPTIBLE; spin_unlock_irqrestore(&cosa->lock, flags); schedule(); spin_lock_irqsave(&cosa->lock, flags); if (signal_pending(current) && chan->rx_status == 0) { chan->rx_status = 1; remove_wait_queue(&chan->rxwaitq, &wait); current->state = TASK_RUNNING; spin_unlock_irqrestore(&cosa->lock, flags); up(&chan->rsem); return -ERESTARTSYS; } } remove_wait_queue(&chan->rxwaitq, &wait); current->state = TASK_RUNNING; kbuf = chan->rxdata; count = chan->rxsize; spin_unlock_irqrestore(&cosa->lock, flags); up(&chan->rsem); if (copy_to_user(buf, kbuf, count)) { kfree(kbuf); return -EFAULT; } kfree(kbuf); return count;}static char *chrdev_setup_rx(struct channel_data *chan, int size){ /* Expect size <= COSA_MTU */ chan->rxsize = size; return chan->rxdata;}static int chrdev_rx_done(struct channel_data *chan){ if (chan->rx_status) { /* Reader has died */ kfree(chan->rxdata); up(&chan->wsem); } chan->rx_status = 1; wake_up_interruptible(&chan->rxwaitq); return 1;}static ssize_t cosa_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos){ DECLARE_WAITQUEUE(wait, current); struct channel_data *chan = file->private_data; struct cosa_data *cosa = chan->cosa; unsigned long flags; char *kbuf; if (!(cosa->firmware_status & COSA_FW_START)) { printk(KERN_NOTICE "%s: start the firmware first (status %d)\n", cosa->name, cosa->firmware_status); return -EPERM; } if (down_interruptible(&chan->wsem)) return -ERESTARTSYS; if (count > COSA_MTU) count = COSA_MTU; /* Allocate the buffer */ if ((kbuf = kmalloc(count, GFP_KERNEL|GFP_DMA)) == NULL) { printk(KERN_NOTICE "%s: cosa_write() OOM - dropping packet\n", cosa->name); up(&chan->wsem); return -ENOMEM; } if (copy_from_user(kbuf, buf, count)) { up(&chan->wsem); kfree(kbuf); return -EFAULT; } chan->tx_status=0; cosa_start_tx(chan, kbuf, count); spin_lock_irqsave(&cosa->lock, flags); add_wait_queue(&chan->txwaitq, &wait); while(!chan->tx_status) { current->state = TASK_INTERRUPTIBLE; spin_unlock_irqrestore(&cosa->lock, flags); schedule(); spin_lock_irqsave(&cosa->lock, flags); if (signal_pending(current) && chan->tx_status == 0) { chan->tx_status = 1; remove_wait_queue(&chan->txwaitq, &wait); current->state = TASK_RUNNING; chan->tx_status = 1; spin_unlock_irqrestore(&cosa->lock, flags); return -ERESTARTSYS; } } remove_wait_queue(&chan->txwaitq, &wait); current->state = TASK_RUNNING; up(&chan->wsem); spin_unlock_irqrestore(&cosa->lock, flags); kfree(kbuf); return count;}static int chrdev_tx_done(struct channel_data *chan, int size){ if (chan->tx_status) { /* Writer was interrupted */ kfree(chan->txbuf); up(&chan->wsem); } chan->tx_status = 1; wake_up_interruptible(&chan->txwaitq); return 1;}static unsigned int cosa_poll(struct file *file, poll_table *poll){ printk(KERN_INFO "cosa_poll is here\n"); return 0;}static int cosa_open(struct inode *inode, struct file *file){ struct cosa_data *cosa; struct channel_data *chan; unsigned long flags; int n; if ((n=iminor(file->f_dentry->d_inode)>>CARD_MINOR_BITS) >= nr_cards) return -ENODEV; cosa = cosa_cards+n; if ((n=iminor(file->f_dentry->d_inode) & ((1<<CARD_MINOR_BITS)-1)) >= cosa->nchannels) return -ENODEV; chan = cosa->chan + n; file->private_data = chan; spin_lock_irqsave(&cosa->lock, flags); if (chan->usage < 0) { /* in netdev mode */ spin_unlock_irqrestore(&cosa->lock, flags); return -EBUSY; } cosa->usage++; chan->usage++; chan->tx_done = chrdev_tx_done; chan->setup_rx = chrdev_setup_rx; chan->rx_done = chrdev_rx_done; spin_unlock_irqrestore(&cosa->lock, flags); return 0;}static int cosa_release(struct inode *inode, struct file *file){ struct channel_data *channel = file->private_data; struct cosa_data *cosa; unsigned long flags; cosa = channel->cosa; spin_lock_irqsave(&cosa->lock, flags); cosa->usage--; channel->usage--; spin_unlock_irqrestore(&cosa->lock, flags); return 0;}#ifdef COSA_FASYNC_WORKINGstatic struct fasync_struct *fasync[256] = { NULL, };/* To be done ... */static int cosa_fasync(struct inode *inode, struct file *file, int on){ int port = iminor(inode); int rv = fasync_helper(inode, file, on, &fasync[port]); return rv < 0 ? rv : 0;}#endif/* ---------- Ioctls ---------- *//* * Ioctl subroutines can safely be made inline, because they are called * only from cosa_ioctl(). */static inline int cosa_reset(struct cosa_data *cosa){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -