📄 ib-usb.c
字号:
/* iBurst (TM) compatible driver for 2.6 Linux kernel. * based on the original ArrayComm (TM) iBurst (TM) driver. * Nicholas Jefferson <nicholas@pythontraining.com.au> * 11 May 2005 * * Ported to 2.6 Linux kernel by David Michael Barr. * Fixes to support new hardware by Shane MacPhillamy. * Fixes to support further new hardware by Nik Trevallyn-Jones. * * 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 "ib-net.h"#include <asm/io.h>#include <linux/version.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/timer.h>#include <linux/usb.h>#define PROTO_USB_CONF 0x03#define VENDOR_USB_REQUEST (USB_DIR_IN | USB_TYPE_VENDOR)#define CONTROL_USB_GET_MAC_ADDR_UT_02 0x63#define CONTROL_USB_GET_MAC_ADDR_UT_04 0x4d#define UT_DEVICE_UT_02 0x0101#define UT_DEVICE_UT_04 0x0401static int debug = 0;module_param(debug, int, 0);#define DEBUG(n, args...) if (debug < (n)) ; else printk(KERN_INFO args)/** * struct ib_usb_priv_t - USB device private state. * @usbdev: USB device state. * @rx_urb: * @tx_urb: * @ctl_buf: buffer for control messages. * @modem: corresponding modem state. */struct ib_usb_priv_t { struct usb_device *usbdev; int rx_endpoint, tx_endpoint; struct urb rx_urb; struct urb tx_urb; unsigned char ctl_buf[32]; struct ib_net_modem_t *modem; short packet_modulo;};/** * ib_usb_conf - USB configuration frame. */static unsigned char ib_usb_conf[] = { 0x00, 0x08, 0x00, 0xf7, IB_PROTO, PROTO_USB_CONF, 0x00, 0x00,};/** * ib_usb_disconnect - USB device disconnected. * @usbintf: */static void ib_usb_disconnect(struct usb_interface *usbintf){ unsigned long state; struct ib_usb_priv_t *priv = usb_get_intfdata(usbintf); spin_lock_irqsave(&ib_lock, state); priv->usbdev = NULL; spin_unlock_irqrestore(&ib_lock, state); if (priv->rx_urb.status == -EINPROGRESS) usb_kill_urb(&priv->rx_urb); if (priv->tx_urb.status == -EINPROGRESS) usb_kill_urb(&priv->tx_urb); ib_net_flush(priv->modem); ib_net_deregister(priv->modem); kfree(priv);}/** * ib_usb_rx_read - receive radio frame. * @urb: * @pt_regs: */#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)static void ib_usb_rx_read(struct urb *urb, struct pt_regs *pt_regs)#elsestatic void ib_usb_rx_read(struct urb *urb)#endif{ unsigned long state; struct ib_usb_priv_t *priv = urb->context; struct ib_net_modem_t *modem = priv->modem; struct ib_net_radio_t *radio = (struct ib_net_radio_t*) modem->rx_buf; int nbuf; spin_lock_irqsave(&ib_lock, state); if (urb->status == 0 && urb->actual_length != 0) { nbuf = IB_NET_NBUF(radio->word[0], radio->word[1]); if (nbuf < IB_NET_RADIO_HEAD || IB_NET_RADIO_HEAD + ETH_DATA_LEN < nbuf) goto failed; ib_net_rx_parse(modem, urb->actual_length); ib_net_schedule(modem); } /* fall through */failed: if (urb->status != -ECONNRESET) { urb->actual_length = 0; urb->dev = priv->usbdev; usb_submit_urb(urb, GFP_ATOMIC); } spin_unlock_irqrestore(&ib_lock, state);}/** * ib_usb_open * called under ib_lock * @_priv: device private state. */static int ib_usb_open(void *_priv){ struct ib_usb_priv_t *priv = _priv; struct ib_net_modem_t *modem = priv->modem; struct ib_net_radio_t *radio; struct sk_buff *skb; skb = alloc_skb(sizeof(ib_usb_conf), GFP_ATOMIC); if (skb == NULL) return -EINVAL; radio = (struct ib_net_radio_t*) skb_put(skb, sizeof(ib_usb_conf)); memcpy(radio, ib_usb_conf, sizeof(ib_usb_conf)); radio->payload[0] = modem->stats.tx_packets & 0xff; radio->payload[1] = (modem->pc_status |= STATUS_READY); skb_queue_tail(&modem->tx_queue, skb); ib_net_schedule(modem); return 0;}/** * ib_usb_rx_parse - * called under ib_lock * @modem: modem private state. */static void ib_usb_rx_parse(void *_priv){ struct ib_usb_priv_t *priv = _priv; struct ib_net_modem_t *modem = priv->modem; struct ib_net_radio_t *radio = (struct ib_net_radio_t*) modem->rx_buf; struct sk_buff *skb; ib_net_ut_status(modem, radio->payload[1]); if (radio->proto[1] == PROTO_USB_CONF) { skb = alloc_skb(sizeof(ib_usb_conf), GFP_ATOMIC); if (skb != NULL) { radio = (struct ib_net_radio_t*) skb_put(skb, sizeof(ib_usb_conf)); memcpy(radio, ib_usb_conf, sizeof(ib_usb_conf)); radio->payload[0] = modem->stats.tx_packets & 0xff; radio->payload[1] = modem->pc_status; skb_queue_tail(&modem->tx_queue, skb); ib_net_schedule(modem); } }}/** * ib_usb_close * called under ib_lock * @modem: modem private state. */static int ib_usb_close(void *_priv){ struct ib_usb_priv_t *priv = _priv; struct ib_net_modem_t *modem = priv->modem; struct ib_net_radio_t *radio; struct sk_buff *skb; if (modem->pc_status & STATUS_READY) { skb = alloc_skb(sizeof(ib_usb_conf), GFP_ATOMIC); if (skb == NULL) return -EINVAL; radio = (struct ib_net_radio_t*) skb_put(skb, sizeof(ib_usb_conf)); memcpy(radio, ib_usb_conf, sizeof(ib_usb_conf)); radio->payload[0] = modem->stats.tx_packets & 0xff; radio->payload[1] = (modem->pc_status &= ~STATUS_READY); skb_queue_tail(&modem->tx_queue, skb); ib_net_schedule(modem); } return 0;}/** * ib_usb_tx_done - transfer completed. * @urb: * @pt_regs: */#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)static void ib_usb_tx_done(struct urb *urb, struct pt_regs *pt_regs)#elsestatic void ib_usb_tx_done(struct urb *urb)#endif{ unsigned long state; struct ib_usb_priv_t *priv = urb->context; spin_lock_irqsave(&ib_lock, state); if (urb->status) priv->modem->stats.tx_errors += 1; if (urb->status != -ECONNRESET) ib_net_schedule(priv->modem); spin_unlock_irqrestore(&ib_lock, state);}/** * ib_usb_poll - poll device. * called under ib_lock * @_priv: device private state. */static void ib_usb_poll(void *_priv){ struct ib_usb_priv_t *priv = _priv; struct ib_net_modem_t *modem = priv->modem; int nbuf, pad_bytes; if (priv->tx_urb.status == -EINPROGRESS) return; nbuf = ib_net_tx_prepare(modem); if (nbuf == 0) return; if (priv->usbdev == NULL) return; usb_fill_bulk_urb(&priv->tx_urb, priv->usbdev, usb_sndbulkpipe(priv->usbdev, priv->tx_endpoint), modem->tx_buf, IB_NET_RADIO_HEAD + ETH_DATA_LEN, ib_usb_tx_done, priv); // NTJ: 1.3.4: avoid modulo-aligned transfers by padding tx_length pad_bytes = (priv->packet_modulo > 0 && (nbuf % priv->packet_modulo == 0) ? 1 : 0); priv->tx_urb.transfer_buffer_length = nbuf + pad_bytes; if (usb_submit_urb(&priv->tx_urb, GFP_KERNEL)) { modem->stats.tx_errors += 1; return; } modem->stats.tx_bytes += nbuf; modem->stats.tx_packets += 1;}/** * ib_usb_tx_timeout * called under ib_lock * @_priv: device private state. */static void ib_usb_tx_timeout(void *_priv){ struct ib_usb_priv_t *priv = _priv; if (priv == NULL) { /* can't happen? */ DEBUG(1,"ib-usb: NULL passed to ib_usb_tx_timeout\n"); return; } if (priv->tx_urb.status == -EINPROGRESS) usb_unlink_urb(&priv->tx_urb);}/** * ib_usb_net_driver - USB driver callback functions. */static struct ib_net_driver_t ib_usb_net_driver = { .poll = ib_usb_poll, .tx_timeout = ib_usb_tx_timeout, .open = ib_usb_open, .rx_parse = ib_usb_rx_parse, .close = ib_usb_close,};/** * ib_usb_probe - probe USB device. * @usbintf: * @id: */static int ib_usb_probe(struct usb_interface *usbintf, const struct usb_device_id *id){ struct usb_device *usbdev = interface_to_usbdev(usbintf); struct usb_host_interface *hostif = usbintf->cur_altsetting; struct ib_usb_priv_t *priv; struct net_device *netdev; struct ib_net_modem_t *modem; struct usb_endpoint_descriptor *ep; int nbuf, err, i; int control; switch (usbdev->descriptor.bcdDevice) { case UT_DEVICE_UT_02: control = CONTROL_USB_GET_MAC_ADDR_UT_02; DEBUG(9, "ib-usb: UT_02 Detected\n"); break; case UT_DEVICE_UT_04: control = CONTROL_USB_GET_MAC_ADDR_UT_04; DEBUG(9, "ib-usb: UT_04 Detected\n"); break; default: DEBUG(1, "ib-usb: UT device %04x\n", usbdev->descriptor.bcdDevice); control = 0x0; //return -ENODEV; } err = usb_reset_configuration(usbdev); if (err) { DEBUG(1, "ib-usb: usb_reset_configuration failed\n"); return err; } priv = kmalloc(sizeof(struct ib_usb_priv_t), GFP_KERNEL); if (priv == NULL) { DEBUG(1, "ib-usb: kmalloc failed\n"); return -ENOMEM; } memset(priv, 0, sizeof(struct ib_usb_priv_t)); /* hack */ priv->usbdev = usbdev; usb_init_urb(&priv->rx_urb); usb_init_urb(&priv->tx_urb); nbuf = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), CONTROL_USB_GET_MAC_ADDR_UT_02, VENDOR_USB_REQUEST, cpu_to_le16(0), cpu_to_le16(0), priv->ctl_buf, cpu_to_le16(8), 0); if (nbuf != 8) { DEBUG(1, "ib-usb: invalid read %02x %02x %02x\n", priv->ctl_buf[0], nbuf, CONTROL_USB_GET_MAC_ADDR_UT_02); err = -ENODEV; goto failed; } // NTJ: 1.3.4: disabled - now determined by endpoint characteristics#if 0 if (priv->ctl_buf[1] != control) { DEBUG(1, "ib-usb: mismatch command %02x %02x\n", priv->ctl_buf[1], control); err = -ENODEV; goto failed; } switch (control) { case CONTROL_USB_GET_MAC_ADDR_UT_02: priv->rx_endpoint = 2; priv->tx_endpoint = 1; break; case CONTROL_USB_GET_MAC_ADDR_UT_04: priv->rx_endpoint = 3; priv->tx_endpoint = 2; break; default: /* can't happen */ err = -ENODEV; goto failed; }#endif /* NTJ: 1.3.4: set the modulo for more recent devices */ if (control == CONTROL_USB_GET_MAC_ADDR_UT_04) { priv->packet_modulo = 64; DEBUG(9, "ib-usb: packet modulo set to %d\n", (int) priv->packet_modulo); } /* * NTJ: 1.3.4: find the endpoints by characteristics, not by descriptor */ priv->rx_endpoint = -1; priv->tx_endpoint = -1; // iterate the endpoints to find the bulk-io endpoints for (i = 0; i < hostif->desc.bNumEndpoints; i++) { ep = &hostif->endpoint[i].desc; if (usb_endpoint_xfer_bulk(ep)) { if (usb_endpoint_dir_in(ep)) { if (priv->rx_endpoint == -1) priv->rx_endpoint = i+1; } else if (usb_endpoint_dir_out(ep)) { if (priv->tx_endpoint == -1) priv->tx_endpoint = i+1; } else { DEBUG(1, "ib-usb: broken Endpoint descr: ep[%d]\n", i); } } if (priv->rx_endpoint != -1 && priv->tx_endpoint != -1) break; } if (priv->rx_endpoint == -1 || priv->tx_endpoint == -1) { DEBUG(1, "ib-usb: One or more bulk-io endpoints not found\n"); return -ENODEV; } DEBUG(9, "ib-usb: rx_endpoint=%d; tx_endpoint=%d\n", priv->rx_endpoint, priv->tx_endpoint); err = ib_net_register(&netdev); if (err) goto failed; modem = netdev->priv; modem->driver = &ib_usb_net_driver; modem->pdriver = priv; priv->modem = modem; ib_net_addr(modem, priv->ctl_buf + 2); usb_fill_bulk_urb(&priv->rx_urb, usbdev, usb_rcvbulkpipe(usbdev, priv->rx_endpoint), modem->rx_buf, IB_NET_RADIO_HEAD + ETH_DATA_LEN, ib_usb_rx_read, priv); priv->rx_urb.actual_length = 0; priv->rx_urb.dev = usbdev; usb_fill_bulk_urb(&priv->tx_urb, usbdev, usb_sndbulkpipe(usbdev, priv->tx_endpoint), modem->tx_buf, IB_NET_RADIO_HEAD + ETH_DATA_LEN, ib_usb_tx_done, priv); priv->tx_urb.transfer_flags |= URB_ZERO_PACKET; usb_set_intfdata(usbintf, priv); usb_submit_urb(&priv->rx_urb, GFP_ATOMIC); return 0;failed: kfree(priv); return err;}/** * ib_usb_table - USB device table. */static struct usb_device_id ib_usb_table[] = { { USB_DEVICE(0x0d14, 0x0009) }, { USB_DEVICE(0x0482, 0x0204) }, {}};MODULE_DEVICE_TABLE(usb, ib_usb_table);/** * ib_usb_driver - USB driver. */static struct usb_driver ib_usb_driver = {#if KERNEL_VERSION(2,4,20) <= LINUX_VERSION_CODE \ && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) .owner = THIS_MODULE,#endif .name = "iburst", .probe = ib_usb_probe, .disconnect = ib_usb_disconnect, .id_table = ib_usb_table,};/** * ib_usb_init - ib-usb module init. */static int __init ib_usb_init(void){ return usb_register(&ib_usb_driver);}/** * ib_usb_exit - ib-usb module exit. */static void __exit ib_usb_exit(void){ usb_deregister(&ib_usb_driver);}module_init(ib_usb_init);module_exit(ib_usb_exit);MODULE_DESCRIPTION("iBurst compatible USB driver");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -