📄 stir4200.c
字号:
/* * Wait for the transmit FIFO to have space for next data * * If space < 0 then wait till FIFO completely drains. * FYI: can take up to 13 seconds at 2400baud. */static int fifo_txwait(struct stir_cb *stir, int space){ int err; unsigned long count, status; /* Read FIFO status and count */ for(;;) { err = read_reg(stir, REG_FIFOCTL, stir->fifo_status, FIFO_REGS_SIZE); if (unlikely(err != FIFO_REGS_SIZE)) { warn("%s: FIFO register read error: %d", stir->netdev->name, err); return err; } status = stir->fifo_status[0]; count = (unsigned)(stir->fifo_status[2] & 0x1f) << 8 | stir->fifo_status[1]; pr_debug("fifo status 0x%lx count %lu\n", status, count); /* error when receive/transmit fifo gets confused */ if (status & FIFOCTL_RXERR) { stir->stats.rx_fifo_errors++; stir->stats.rx_errors++; break; } if (status & FIFOCTL_TXERR) { stir->stats.tx_fifo_errors++; stir->stats.tx_errors++; break; } /* is fifo receiving already, or empty */ if (!(status & FIFOCTL_DIR) || (status & FIFOCTL_EMPTY)) return 0; if (signal_pending(current)) return -EINTR; /* shutting down? */ if (!netif_running(stir->netdev) || !netif_device_present(stir->netdev)) return -ESHUTDOWN; /* only waiting for some space */ if (space >= 0 && STIR_FIFO_SIZE - 4 > space + count) return 0; /* estimate transfer time for remaining chars */ msleep((count * 8000) / stir->speed); } err = write_reg(stir, REG_FIFOCTL, FIFOCTL_CLR); if (err) return err; err = write_reg(stir, REG_FIFOCTL, 0); if (err) return err; return 0;}/* Wait for turnaround delay before starting transmit. */static void turnaround_delay(const struct stir_cb *stir, long us){ long ticks; struct timeval now; if (us <= 0) return; do_gettimeofday(&now); us -= (now.tv_sec - stir->rx_time.tv_sec) * USEC_PER_SEC; us -= now.tv_usec - stir->rx_time.tv_usec; if (us < 10) return; ticks = us / (1000000 / HZ); if (ticks > 0) { current->state = TASK_INTERRUPTIBLE; schedule_timeout(1 + ticks); } else udelay(us);}/* * Start receiver by submitting a request to the receive pipe. * If nothing is available it will return after rx_interval. */static int receive_start(struct stir_cb *stir){ /* reset state */ stir->receiving = 1; stir->rx_buff.in_frame = FALSE; stir->rx_buff.state = OUTSIDE_FRAME; stir->rx_urb->status = 0; return usb_submit_urb(stir->rx_urb, GFP_KERNEL);}/* Stop all pending receive Urb's */static void receive_stop(struct stir_cb *stir){ stir->receiving = 0; usb_unlink_urb(stir->rx_urb); if (stir->rx_buff.in_frame) stir->stats.collisions++;}/* * Wrap data in socket buffer and send it. */static void stir_send(struct stir_cb *stir, struct sk_buff *skb){ unsigned wraplen; int first_frame = 0; /* if receiving, need to turnaround */ if (stir->receiving) { receive_stop(stir); turnaround_delay(stir, irda_get_mtt(skb)); first_frame = 1; } if (isfir(stir->speed)) wraplen = wrap_fir_skb(skb, stir->io_buf); else wraplen = wrap_sir_skb(skb, stir->io_buf); /* check for space available in fifo */ if (!first_frame) fifo_txwait(stir, wraplen); stir->stats.tx_packets++; stir->stats.tx_bytes += skb->len; stir->netdev->trans_start = jiffies; pr_debug("send %d (%d)\n", skb->len, wraplen); if (usb_bulk_msg(stir->usbdev, usb_sndbulkpipe(stir->usbdev, 1), stir->io_buf, wraplen, NULL, msecs_to_jiffies(TRANSMIT_TIMEOUT))) stir->stats.tx_errors++;}/* * Transmit state machine thread */static int stir_transmit_thread(void *arg){ struct stir_cb *stir = arg; struct net_device *dev = stir->netdev; struct sk_buff *skb; daemonize("%s", dev->name); allow_signal(SIGTERM); while (netif_running(dev) && netif_device_present(dev) && !signal_pending(current)) { /* if suspending, then power off and wait */ if (current->flags & PF_FREEZE) { if (stir->receiving) receive_stop(stir); else fifo_txwait(stir, -1); write_reg(stir, REG_CTRL1, CTRL1_TXPWD|CTRL1_RXPWD); refrigerator(PF_FREEZE); if (change_speed(stir, stir->speed)) break; } /* if something to send? */ skb = xchg(&stir->tx_pending, NULL); if (skb) { unsigned new_speed = irda_get_next_speed(skb); netif_wake_queue(dev); if (skb->len > 0) stir_send(stir, skb); dev_kfree_skb(skb); if (stir->speed != new_speed) { if (fifo_txwait(stir, -1) || change_speed(stir, new_speed)) break; } continue; } /* nothing to send? start receiving */ if (!stir->receiving && irda_device_txqueue_empty(dev)) { /* Wait otherwise chip gets confused. */ if (fifo_txwait(stir, -1)) break; if (unlikely(receive_start(stir))) { if (net_ratelimit()) info("%s: receive usb submit failed", stir->netdev->name); stir->receiving = 0; msleep(10); continue; } } /* sleep if nothing to send */ wait_event_interruptible(stir->thr_wait, stir->tx_pending); } complete_and_exit (&stir->thr_exited, 0);}/* * USB bulk receive completion callback. * Wakes up every ms (usb round trip) with wrapped * data. */static void stir_rcv_irq(struct urb *urb, struct pt_regs *regs){ struct stir_cb *stir = urb->context; int err; /* in process of stopping, just drop data */ if (!netif_running(stir->netdev)) return; /* unlink, shutdown, unplug, other nasties */ if (urb->status != 0) return; if (urb->actual_length > 0) { pr_debug("receive %d\n", urb->actual_length); unwrap_chars(stir, urb->transfer_buffer, urb->actual_length); stir->netdev->last_rx = jiffies; do_gettimeofday(&stir->rx_time); } /* kernel thread is stopping receiver don't resubmit */ if (!stir->receiving) return; /* resubmit existing urb */ err = usb_submit_urb(urb, GFP_ATOMIC); /* in case of error, the kernel thread will restart us */ if (err) { warn("%s: usb receive submit error: %d", stir->netdev->name, err); stir->receiving = 0; wake_up(&stir->thr_wait); }}/* * Function stir_net_open (dev) * * Network device is taken up. Usually this is done by "ifconfig irda0 up" */static int stir_net_open(struct net_device *netdev){ struct stir_cb *stir = netdev->priv; int err; char hwname[16]; err = usb_clear_halt(stir->usbdev, usb_sndbulkpipe(stir->usbdev, 1)); if (err) goto err_out1; err = usb_clear_halt(stir->usbdev, usb_rcvbulkpipe(stir->usbdev, 2)); if (err) goto err_out1; err = change_speed(stir, 9600); if (err) goto err_out1; err = -ENOMEM; /* Initialize for SIR/FIR to copy data directly into skb. */ stir->receiving = 0; stir->rx_buff.truesize = IRDA_SKB_MAX_MTU; stir->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); if (!stir->rx_buff.skb) goto err_out1; skb_reserve(stir->rx_buff.skb, 1); stir->rx_buff.head = stir->rx_buff.skb->data; do_gettimeofday(&stir->rx_time); stir->rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!stir->rx_urb) goto err_out2; stir->io_buf = kmalloc(STIR_FIFO_SIZE, GFP_KERNEL); if (!stir->io_buf) goto err_out3; usb_fill_bulk_urb(stir->rx_urb, stir->usbdev, usb_rcvbulkpipe(stir->usbdev, 2), stir->io_buf, STIR_FIFO_SIZE, stir_rcv_irq, stir); stir->fifo_status = kmalloc(FIFO_REGS_SIZE, GFP_KERNEL); if (!stir->fifo_status) goto err_out4; /* * Now that everything should be initialized properly, * Open new IrLAP layer instance to take care of us... * Note : will send immediately a speed change... */ sprintf(hwname, "usb#%d", stir->usbdev->devnum); stir->irlap = irlap_open(netdev, &stir->qos, hwname); if (!stir->irlap) { err("irlap_open failed"); goto err_out5; } /** Start kernel thread for transmit. */ stir->thr_pid = kernel_thread(stir_transmit_thread, stir, CLONE_FS|CLONE_FILES); if (stir->thr_pid < 0) { err = stir->thr_pid; err("unable to start kernel thread"); goto err_out6; } netif_start_queue(netdev); return 0; err_out6: irlap_close(stir->irlap); err_out5: kfree(stir->fifo_status); err_out4: kfree(stir->io_buf); err_out3: usb_free_urb(stir->rx_urb); err_out2: kfree_skb(stir->rx_buff.skb); err_out1: return err;}/* * Function stir_net_close (stir) * * Network device is taken down. Usually this is done by * "ifconfig irda0 down" */static int stir_net_close(struct net_device *netdev){ struct stir_cb *stir = netdev->priv; /* Stop transmit processing */ netif_stop_queue(netdev); /* Kill transmit thread */ kill_proc(stir->thr_pid, SIGTERM, 1); wait_for_completion(&stir->thr_exited); kfree(stir->fifo_status); /* Mop up receive urb's */ usb_unlink_urb(stir->rx_urb); kfree(stir->io_buf); usb_free_urb(stir->rx_urb); kfree_skb(stir->rx_buff.skb); /* Stop and remove instance of IrLAP */ if (stir->irlap) irlap_close(stir->irlap); stir->irlap = NULL; return 0;}/* * IOCTLs : Extra out-of-band network commands... */static int stir_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){ struct if_irda_req *irq = (struct if_irda_req *) rq; struct stir_cb *stir = dev->priv; int ret = 0; switch (cmd) { case SIOCSBANDWIDTH: /* Set bandwidth */ if (!capable(CAP_NET_ADMIN)) return -EPERM; /* Check if the device is still there */ if (netif_device_present(stir->netdev)) ret = change_speed(stir, irq->ifr_baudrate); break; case SIOCSMEDIABUSY: /* Set media busy */ if (!capable(CAP_NET_ADMIN)) return -EPERM; /* Check if the IrDA stack is still there */ if (netif_running(stir->netdev)) irda_device_set_media_busy(stir->netdev, TRUE); break; case SIOCGRECEIVING: /* Only approximately true */ irq->ifr_receiving = stir->receiving; break; default: ret = -EOPNOTSUPP; } return ret;}/* * Get device stats (for /proc/net/dev and ifconfig) */static struct net_device_stats *stir_net_get_stats(struct net_device *dev){ struct stir_cb *stir = dev->priv; return &stir->stats;}/* * This routine is called by the USB subsystem for each new device * in the system. We need to check if the device is ours, and in * this case start handling it. * Note : it might be worth protecting this function by a global * spinlock... Or not, because maybe USB already deal with that... */static int stir_probe(struct usb_interface *intf, const struct usb_device_id *id){ struct usb_device *dev = interface_to_usbdev(intf); struct stir_cb *stir = NULL; struct net_device *net; int ret = -ENOMEM; /* Allocate network device container. */ net = alloc_irdadev(sizeof(*stir)); if(!net) goto err_out1; SET_MODULE_OWNER(net); SET_NETDEV_DEV(net, &intf->dev); stir = net->priv; stir->netdev = net; stir->usbdev = dev; ret = usb_reset_configuration(dev); if (ret != 0) { err("usb reset configuration failed"); goto err_out2; } printk(KERN_INFO "SigmaTel STIr4200 IRDA/USB found at address %d, " "Vendor: %x, Product: %x\n", dev->devnum, dev->descriptor.idVendor, dev->descriptor.idProduct); /* Initialize QoS for this device */ irda_init_max_qos_capabilies(&stir->qos); /* That's the Rx capability. */ stir->qos.baud_rate.bits &= IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200 | (IR_4000000 << 8); stir->qos.min_turn_time.bits &= qos_mtt_bits; irda_qos_bits_to_value(&stir->qos); init_completion (&stir->thr_exited); init_waitqueue_head (&stir->thr_wait); /* Override the network functions we need to use */ net->hard_start_xmit = stir_hard_xmit; net->open = stir_net_open; net->stop = stir_net_close; net->get_stats = stir_net_get_stats; net->do_ioctl = stir_net_ioctl; ret = register_netdev(net); if (ret != 0) goto err_out2; MESSAGE("IrDA: Registered SigmaTel device %s\n", net->name); usb_set_intfdata(intf, stir); return 0;err_out2: free_netdev(net);err_out1: return ret;}/* * The current device is removed, the USB layer tell us to shut it down... */static void stir_disconnect(struct usb_interface *intf){ struct stir_cb *stir = usb_get_intfdata(intf); if (!stir) return; unregister_netdev(stir->netdev); free_netdev(stir->netdev); usb_set_intfdata(intf, NULL);}/* Power management suspend, so power off the transmitter/receiver */static int stir_suspend(struct usb_interface *intf, u32 state){ struct stir_cb *stir = usb_get_intfdata(intf); netif_device_detach(stir->netdev); return 0;}/* Coming out of suspend, so reset hardware */static int stir_resume(struct usb_interface *intf){ struct stir_cb *stir = usb_get_intfdata(intf); netif_device_attach(stir->netdev); /* receiver restarted when send thread wakes up */ return 0;}/* * USB device callbacks */static struct usb_driver irda_driver = { .owner = THIS_MODULE, .name = "stir4200", .probe = stir_probe, .disconnect = stir_disconnect, .id_table = dongles, .suspend = stir_suspend, .resume = stir_resume,};/* * Module insertion */static int __init stir_init(void){ if (usb_register(&irda_driver) < 0) return -1; MESSAGE("SigmaTel support registered\n"); return 0;}module_init(stir_init);/* * Module removal */static void __exit stir_cleanup(void){ /* Deregister the driver and remove all pending instances */ usb_deregister(&irda_driver);}module_exit(stir_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -