📄 mcs7780.c
字号:
if (!mcs->tx_urb) return 0; mcs->rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!mcs->rx_urb) return 0; return 1;}/* Sets up state to be initially outside frame, gets receive urb, * sets status to successful and then submits the urb to start * receiving the data. */static inline int mcs_receive_start(struct mcs_cb *mcs){ mcs->rx_buff.in_frame = FALSE; mcs->rx_buff.state = OUTSIDE_FRAME; usb_fill_bulk_urb(mcs->rx_urb, mcs->usbdev, usb_rcvbulkpipe(mcs->usbdev, mcs->ep_in), mcs->in_buf, 4096, mcs_receive_irq, mcs); mcs->rx_urb->status = 0; return usb_submit_urb(mcs->rx_urb, GFP_KERNEL);}/* Finds the in and out endpoints for the mcs control block */static inline int mcs_find_endpoints(struct mcs_cb *mcs, struct usb_host_endpoint *ep, int epnum){ int i; int ret = 0; /* If no place to store the endpoints just return */ if (!ep) return ret; /* cycle through all endpoints, find the first two that are DIR_IN */ for (i = 0; i < epnum; i++) { if (ep[i].desc.bEndpointAddress & USB_DIR_IN) mcs->ep_in = ep[i].desc.bEndpointAddress; else mcs->ep_out = ep[i].desc.bEndpointAddress; /* MosChip says that the chip has only two bulk * endpoints. Find one for each direction and move on. */ if ((mcs->ep_in != 0) && (mcs->ep_out != 0)) { ret = 1; break; } } return ret;}static void mcs_speed_work(struct work_struct *work){ struct mcs_cb *mcs = container_of(work, struct mcs_cb, work); struct net_device *netdev = mcs->netdev; mcs_speed_change(mcs); netif_wake_queue(netdev);}/* Function to change the speed of the mcs7780. Fully supports SIR, * MIR, and FIR speeds. */static int mcs_speed_change(struct mcs_cb *mcs){ int ret = 0; int rst = 0; int cnt = 0; __u16 nspeed; __u16 rval; nspeed = mcs_speed_set[(mcs->new_speed >> 8) & 0x0f]; do { mcs_get_reg(mcs, MCS_RESV_REG, &rval); } while(cnt++ < 100 && (rval & MCS_IRINTX)); if(cnt >= 100) { IRDA_ERROR("unable to change speed"); ret = -EIO; goto error; } mcs_get_reg(mcs, MCS_MODE_REG, &rval); /* MINRXPW values recomended by MosChip */ if (mcs->new_speed <= 115200) { rval &= ~MCS_FIR; if ((rst = (mcs->speed > 115200))) mcs_set_reg(mcs, MCS_MINRXPW_REG, 0); } else if (mcs->new_speed <= 1152000) { rval &= ~MCS_FIR; if ((rst = !(mcs->speed == 576000 || mcs->speed == 1152000))) mcs_set_reg(mcs, MCS_MINRXPW_REG, 5); } else { rval |= MCS_FIR; if ((rst = (mcs->speed != 4000000))) mcs_set_reg(mcs, MCS_MINRXPW_REG, 5); } rval &= ~MCS_SPEED_MASK; rval |= nspeed; ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); if (unlikely(ret)) goto error; if (rst) switch (mcs->transceiver_type) { case MCS_TSC_VISHAY: ret = mcs_setup_transceiver_vishay(mcs); break; case MCS_TSC_SHARP: ret = mcs_setup_transceiver_sharp(mcs); break; case MCS_TSC_AGILENT: ret = mcs_setup_transceiver_agilent(mcs); break; default: ret = 1; IRDA_WARNING("Unknown transceiver type: %d", mcs->transceiver_type); } if (unlikely(ret)) goto error; mcs_get_reg(mcs, MCS_MODE_REG, &rval); rval &= ~MCS_RESET; ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); mcs->speed = mcs->new_speed; error: mcs->new_speed = 0; return ret;}/* Ioctl calls not supported at this time. Can be an area of future work. */static int mcs_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd){ /* struct if_irda_req *irq = (struct if_irda_req *)rq; */ /* struct mcs_cb *mcs = netdev_priv(netdev); */ int ret = 0; switch (cmd) { default: ret = -EOPNOTSUPP; } return ret;}/* Network device is taken down, done by "ifconfig irda0 down" */static int mcs_net_close(struct net_device *netdev){ int ret = 0; struct mcs_cb *mcs = netdev_priv(netdev); /* Stop transmit processing */ netif_stop_queue(netdev); /* kill and free the receive and transmit URBs */ usb_kill_urb(mcs->rx_urb); usb_free_urb(mcs->rx_urb); usb_kill_urb(mcs->tx_urb); usb_free_urb(mcs->tx_urb); /* Stop and remove instance of IrLAP */ if (mcs->irlap) irlap_close(mcs->irlap); mcs->irlap = NULL; return ret;}/* Network device is taken up, done by "ifconfig irda0 up" */static int mcs_net_open(struct net_device *netdev){ struct mcs_cb *mcs = netdev_priv(netdev); char hwname[16]; int ret = 0; ret = usb_clear_halt(mcs->usbdev, usb_sndbulkpipe(mcs->usbdev, mcs->ep_in)); if (ret) goto error1; ret = usb_clear_halt(mcs->usbdev, usb_rcvbulkpipe(mcs->usbdev, mcs->ep_out)); if (ret) goto error1; ret = mcs_setup_transceiver(mcs); if (ret) goto error1; ret = -ENOMEM; /* Initialize for SIR/FIR to copy data directly into skb. */ mcs->receiving = 0; mcs->rx_buff.truesize = IRDA_SKB_MAX_MTU; mcs->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); if (!mcs->rx_buff.skb) goto error1; skb_reserve(mcs->rx_buff.skb, 1); mcs->rx_buff.head = mcs->rx_buff.skb->data; do_gettimeofday(&mcs->rx_time); /* * 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", mcs->usbdev->devnum); mcs->irlap = irlap_open(netdev, &mcs->qos, hwname); if (!mcs->irlap) { IRDA_ERROR("mcs7780: irlap_open failed"); goto error2; } if (!mcs_setup_urbs(mcs)) goto error3; ret = mcs_receive_start(mcs); if (ret) goto error3; netif_start_queue(netdev); return 0; error3: irlap_close(mcs->irlap); error2: kfree_skb(mcs->rx_buff.skb); error1: return ret;}/* Get device stats for /proc/net/dev and ifconfig */static struct net_device_stats *mcs_net_get_stats(struct net_device *netdev){ struct mcs_cb *mcs = netdev_priv(netdev); return &mcs->stats;}/* Receive callback function. */static void mcs_receive_irq(struct urb *urb){ __u8 *bytes; struct mcs_cb *mcs = urb->context; int i; int ret; if (!netif_running(mcs->netdev)) return; if (urb->status) return; if (urb->actual_length > 0) { bytes = urb->transfer_buffer; /* MCS returns frames without BOF and EOF * I assume it returns whole frames. */ /* SIR speed */ if(mcs->speed < 576000) { async_unwrap_char(mcs->netdev, &mcs->stats, &mcs->rx_buff, 0xc0); for (i = 0; i < urb->actual_length; i++) async_unwrap_char(mcs->netdev, &mcs->stats, &mcs->rx_buff, bytes[i]); async_unwrap_char(mcs->netdev, &mcs->stats, &mcs->rx_buff, 0xc1); } /* MIR speed */ else if(mcs->speed == 576000 || mcs->speed == 1152000) { mcs_unwrap_mir(mcs, urb->transfer_buffer, urb->actual_length); } /* FIR speed */ else { mcs_unwrap_fir(mcs, urb->transfer_buffer, urb->actual_length); } mcs->netdev->last_rx = jiffies; do_gettimeofday(&mcs->rx_time); } ret = usb_submit_urb(urb, GFP_ATOMIC);}/* Transmit callback funtion. */static void mcs_send_irq(struct urb *urb){ struct mcs_cb *mcs = urb->context; struct net_device *ndev = mcs->netdev; if (unlikely(mcs->new_speed)) schedule_work(&mcs->work); else netif_wake_queue(ndev);}/* Transmit callback funtion. */static int mcs_hard_xmit(struct sk_buff *skb, struct net_device *ndev){ unsigned long flags; struct mcs_cb *mcs; int wraplen; int ret = 0; if (skb == NULL || ndev == NULL) return -EINVAL; netif_stop_queue(ndev); mcs = netdev_priv(ndev); spin_lock_irqsave(&mcs->lock, flags); mcs->new_speed = irda_get_next_speed(skb); if (likely(mcs->new_speed == mcs->speed)) mcs->new_speed = 0; /* SIR speed */ if(mcs->speed < 576000) { wraplen = mcs_wrap_sir_skb(skb, mcs->out_buf); } /* MIR speed */ else if(mcs->speed == 576000 || mcs->speed == 1152000) { wraplen = mcs_wrap_mir_skb(skb, mcs->out_buf); } /* FIR speed */ else { wraplen = mcs_wrap_fir_skb(skb, mcs->out_buf); } usb_fill_bulk_urb(mcs->tx_urb, mcs->usbdev, usb_sndbulkpipe(mcs->usbdev, mcs->ep_out), mcs->out_buf, wraplen, mcs_send_irq, mcs); if ((ret = usb_submit_urb(mcs->tx_urb, GFP_ATOMIC))) { IRDA_ERROR("failed tx_urb: %d", ret); switch (ret) { case -ENODEV: case -EPIPE: break; default: mcs->stats.tx_errors++; netif_start_queue(ndev); } } else { mcs->stats.tx_packets++; mcs->stats.tx_bytes += skb->len; } dev_kfree_skb(skb); spin_unlock_irqrestore(&mcs->lock, flags); return ret;}/* * This function is called by the USB subsystem for each new device in the * system. Need to verify the device and if it is, then start handling it. */static int mcs_probe(struct usb_interface *intf, const struct usb_device_id *id){ struct usb_device *udev = interface_to_usbdev(intf); struct net_device *ndev = NULL; struct mcs_cb *mcs; int ret = -ENOMEM; ndev = alloc_irdadev(sizeof(*mcs)); if (!ndev) goto error1; IRDA_DEBUG(1, "MCS7780 USB-IrDA bridge found at %d.", udev->devnum); /* what is it realy for? */ SET_MODULE_OWNER(ndev); SET_NETDEV_DEV(ndev, &intf->dev); ret = usb_reset_configuration(udev); if (ret != 0) { IRDA_ERROR("mcs7780: usb reset configuration failed"); goto error2; } mcs = netdev_priv(ndev); mcs->usbdev = udev; mcs->netdev = ndev; spin_lock_init(&mcs->lock); /* Initialize QoS for this device */ irda_init_max_qos_capabilies(&mcs->qos); /* That's the Rx capability. */ mcs->qos.baud_rate.bits &= IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200 | IR_576000 | IR_1152000 | (IR_4000000 << 8); mcs->qos.min_turn_time.bits &= qos_mtt_bits; irda_qos_bits_to_value(&mcs->qos); /* Speed change work initialisation*/ INIT_WORK(&mcs->work, mcs_speed_work); /* Override the network functions we need to use */ ndev->hard_start_xmit = mcs_hard_xmit; ndev->open = mcs_net_open; ndev->stop = mcs_net_close; ndev->get_stats = mcs_net_get_stats; ndev->do_ioctl = mcs_net_ioctl; if (!intf->cur_altsetting) goto error2; ret = mcs_find_endpoints(mcs, intf->cur_altsetting->endpoint, intf->cur_altsetting->desc.bNumEndpoints); if (!ret) { ret = -ENODEV; goto error2; } ret = register_netdev(ndev); if (ret != 0) goto error2; IRDA_DEBUG(1, "IrDA: Registered MosChip MCS7780 device as %s", ndev->name); mcs->transceiver_type = transceiver_type; mcs->sir_tweak = sir_tweak; mcs->receive_mode = receive_mode; usb_set_intfdata(intf, mcs); return 0; error2: free_netdev(ndev); error1: return ret;}/* The current device is removed, the USB layer tells us to shut down. */static void mcs_disconnect(struct usb_interface *intf){ struct mcs_cb *mcs = usb_get_intfdata(intf); if (!mcs) return; flush_scheduled_work(); unregister_netdev(mcs->netdev); free_netdev(mcs->netdev); usb_set_intfdata(intf, NULL); IRDA_DEBUG(0, "MCS7780 now disconnected.");}/* Module insertion */static int __init mcs_init(void){ int result; /* register this driver with the USB subsystem */ result = usb_register(&mcs_driver); if (result) IRDA_ERROR("usb_register failed. Error number %d", result); return result;}module_init(mcs_init);/* Module removal */static void __exit mcs_exit(void){ /* deregister this driver with the USB subsystem */ usb_deregister(&mcs_driver);}module_exit(mcs_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -