diff -Nru a/drivers/usb/Config.in b/drivers/usb/Config.in --- a/drivers/usb/Config.in Wed Dec 6 09:56:07 2000 +++ b/drivers/usb/Config.in Wed Dec 6 09:56:07 2000 @@ -74,6 +74,7 @@ dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV dep_tristate ' USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB dep_tristate ' NetChip 1080-based USB Host-to-Host Link (EXPERIMENTAL)' CONFIG_USB_NET1080 $CONFIG_USB $CONFIG_NET + dep_tristate ' USB net-host (EXPERIMENTAL)' CONFIG_USB_NET_HOST $CONFIG_USB fi comment 'USB Human Interface Devices (HID)' diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile --- a/drivers/usb/Makefile Wed Dec 6 09:56:07 2000 +++ b/drivers/usb/Makefile Wed Dec 6 09:56:07 2000 @@ -65,6 +65,7 @@ obj-$(CONFIG_USB_MICROTEK) += microtek.o obj-$(CONFIG_USB_BLUETOOTH) += bluetooth.o obj-$(CONFIG_USB_NET1080) += net1080.o +obj-$(CONFIG_USB_NET_HOST) += usb-net-host.o # Object files in subdirectories diff -Nru a/drivers/usb/usb-net-host.c b/drivers/usb/usb-net-host.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/usb-net-host.c Wed Dec 6 09:56:07 2000 @@ -0,0 +1,810 @@ +/* + * net host usb device + * + * Provides a simple ethernet-like network device interface to the + * usb bus. Packets are sent using bulk xfer and recieved using interrupt + * requests. + * + * version 0.3: slightly cleaned up 8/25/00 brad@parker.boston.ma.us + * todo: + * - make recieve use bulk also with interrupt just for status + * - allow for multiple interfaces + * + * Based on + * Itsy Pocket Computer Host Driver based on Armin Fuerst's Abstract Control + * Model which is apparently based on Brad Keryan's USB busmouse driver . + * + * version 0.2: Improved Bulk transfer. TX led now flashes every time data is + * sent. Send Encapsulated Data is not needed, nor does it do anything. + * Why's that ?!? Thanks to Thomas Sailer for his close look at the bulk code. + * He told me about some important bugs. (5/21/99) + * + * version 0.1: Bulk transfer for uhci seems to work now, no dangling tds any + * more. TX led of the ISDN TA flashed the first time. Does this mean it works? + * The interrupt of the ctrl endpoint crashes the kernel => no read possible + * (5/19/99) + * + * version 0.0: Driver sets up configuration, sets up data pipes, opens misc + * device. No actual data transfer is done, since we don't have bulk transfer, + * yet. Purely skeleton for now. (5/8/99) + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include /* struct net_device, and other headers */ +#include /* eth_type_trans */ +#include /* struct iphdr */ +#include /* struct tcphdr */ +#include + +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#define V22 +#define net_device_stats enet_statistics +#endif + +#define USB_VENDOR_COMPAQ 1183 +#define COMPAQ_ITSY_ID 0x505A + +#define ID_VENDOR USB_VENDOR_COMPAQ +#define ID_PRODUCT COMPAQ_ITSY_ID + +enum { + NHS_RUNNING, + NHS_STALLED +}; + +/* + This structure initialised to zero unless explicitly initialised from + usb_net_host_init(void) +*/ +struct net_host_state { + struct net_host_state *next; + int present; /* this acm is plugged in */ + int state; + + struct net_device *net_dev; + struct net_device_stats eth_stats; + char client_addr[ETH_ALEN]; + + struct urb *int_urb; + struct urb *bulk_urb; + int read_interval; + + struct sk_buff *recv_skb; + + struct tq_struct ctrl_task; + struct usb_device *usb_dev; + char *readbuffer; /*buffer for control messages*/ + char *writebuffer; + int writing, reading; + + unsigned int readendp,writeendp,ctrlendp; + unsigned int readpipe,writepipe,ctrlpipe; + unsigned int pktlen; /* decoded from USB */ +}; +static struct net_host_state static_net_host_state; + +spinlock_t usb_net_host_lock = SPIN_LOCK_UNLOCKED; + +/* + * Open and close + */ +int +net_host_open(struct net_device *net_dev) +{ + int retval = 0; + + printk("net_host_open\n"); + +#ifdef V22 + net_dev->tbusy = 0; + net_dev->start = 1; +#else + netif_start_queue(net_dev); +#endif + MOD_INC_USE_COUNT; + + return retval; +} + +int +net_host_release(struct net_device *net_dev) +{ + printk("net_host_release\n"); + +#ifdef V22 + if(net_dev->start == 0) + return 0; + + net_dev->tbusy = 1; + net_dev->start = 0; +#else + netif_stop_queue(net_dev); +#endif + MOD_DEC_USE_COUNT; + + + return 0; +} + +/* + * Configuration changes (passed on by ifconfig) + */ +int +net_host_config(struct net_device *net_dev, struct ifmap *map) +{ + printk("net_host_config\n"); + + /* can't act on a running interface */ + if (net_dev->flags & IFF_UP) + { + return -EBUSY; + } + + /* ignore other fields */ + return 0; +} + +/* + * Handles control requests, which are initiated in irq handlers. + * Because the control request runs synchronously and requires the interrupt + * this needs to be done outside the context of an irq handler. + * + * The data is an integer USB status code. So far the endpoint may be + * determined from the status code. + */ +static void +net_host_ctrl(void *data) +{ + struct net_host_state *nhs = data; + + printk("net_host_ctrl(nhs=%p)\n", nhs); + printk("state=%d; usb_dev %p ep %d\n", + nhs->state, nhs->usb_dev, nhs->writeendp); + + switch (nhs->state) + { + case NHS_STALLED: + printk("clearing\n"); + usb_clear_halt(nhs->usb_dev, nhs->writepipe/* | USB_DIR_OUT*/); + printk("cleared\n"); + + nhs->writing = 0; + nhs->state = NHS_RUNNING; + +#ifdef V22 + nhs->net_dev->tbusy = 0; + mark_bh(NET_BH); +#else + if (netif_queue_stopped(nhs->net_dev)) + netif_wake_queue(nhs->net_dev); +#endif + break; + + default: + break; + } +} + +/* + * Transmit interrupt handler, checks the status and potentially + * clears the pipe if an error occurred. + */ +static void +net_host_tx_irq(struct urb *urb) +{ + struct net_host_state *nhs = urb->context; + + nhs->bulk_urb = NULL; + + if (urb->status == USB_ST_STALL) + { + printk("net_host_tx_irq() urb_status %x (stalled)\n", urb->status); + /* reset the pipe, I cannot make the synchronous call from + * inside the irq handler so I shedule the task for later. + */ + if (nhs->ctrl_task.sync == 0) + { + nhs->state = NHS_STALLED; + nhs->ctrl_task.routine = net_host_ctrl; + nhs->ctrl_task.data = nhs; + schedule_task(&nhs->ctrl_task); + } + else + { + printk("couldn't clear tx stall\n"); + } + } + else + { + nhs->writing = 0; + if (nhs->net_dev) { +#ifdef V22 + nhs->net_dev->tbusy = 0; + mark_bh(NET_BH); +#else + if (netif_queue_stopped(nhs->net_dev)) + netif_wake_queue(nhs->net_dev); +#endif + } + } +} + +/* + * Transmit a packet (called by the kernel) + */ +int +net_host_tx(struct sk_buff *skb, struct net_device *net_dev) +{ + struct net_host_state *nhs = (struct net_host_state *)net_dev->priv; + unsigned int send_len; + int ret; + +#ifdef V22 + if (net_dev->tbusy) + { + /* XXX use jiffies to time out the previous send. */ + if (nhs->bulk_urb) { + printk("busy; bulk_urb->status %d\n", nhs->bulk_urb->status); + } + + return -EBUSY; + } +#endif + + if (nhs->present) + { +#ifdef V22 + net_dev->tbusy = 1; /* transmission is busy */ +#endif + net_dev->trans_start = jiffies; /* save the timestamp */ + + netif_stop_queue(net_dev); + + /* copy the buffer into the send page... + * I can't corrupt the actual skb because + * it is used after I am finished with it, so I make + * a copy. + */ + send_len = skb->len - 10; + + + if (send_len > PAGE_SIZE) { + printk("usb_net_host: send len (%d) > PAGE_SIZE!\n", send_len); + send_len = PAGE_SIZE; + } + + memcpy(nhs->writebuffer, (skb->data+10), send_len); + + nhs->writebuffer[0] = (char) (send_len & 0x00ff); + nhs->writebuffer[1] = (char) ((send_len & 0xff00) >> 8); + + /* allocate, fill and submit an urb for the bulk xfer */ + nhs->bulk_urb = usb_alloc_urb(0); + if (!nhs->bulk_urb) { + printk("usb_net_host: couldn't allocate bulk urb\n"); + goto tx_err; + } + + FILL_BULK_URB(nhs->bulk_urb, nhs->usb_dev, nhs->writepipe, + nhs->writebuffer, send_len, + net_host_tx_irq, nhs); + +#if 0 + /* ------- print the raw TX packet ---------- */ + { + unsigned i,len; + len = (send_len > 40 ) ? 40 : send_len; + printk("TX:[%d]",send_len); + for(i=0 ; i < len ; i++) + printk("%02x:",(unsigned char) *(nhs->writebuffer + i) ); + printk("\n"); + } + /* ------- print the raw packet ---------- */ +#endif + + ret = usb_submit_urb(nhs->bulk_urb); + if (ret) { + printk("usb_net_host: usb_submit_urb failed (%d)\n", ret); + tx_err: + nhs->eth_stats.tx_errors++; + dev_kfree_skb(skb); + return 0; + } + + nhs->eth_stats.tx_packets++; + nhs->eth_stats.tx_bytes += skb->len; + } else { + nhs->eth_stats.tx_dropped++; + } + dev_kfree_skb(skb); + return 0; +} + +/* + * Ioctl commands + */ +int +net_host_ioctl(struct net_device *net_dev, struct ifreq *rq, int cmd) +{ +// printk("net_host_ioctl\n"); + + return 0; +} + +/* + * Return statistics to the caller + */ +struct net_device_stats * +net_host_stats(struct net_device *net_dev) +{ + struct net_host_state *priv = (struct net_host_state *) net_dev->priv; + struct net_device_stats *stats=NULL; + +// printk("net_host_stats\n"); + if (priv) + { + stats = &priv->eth_stats; + } + return stats; +} + +/* + * The init function (sometimes called probe). + * It is invoked by register_netdev() + */ +static int +net_dev_init(struct net_device *net_dev) +{ + struct net_host_state *nhs = (struct net_host_state *)net_dev->priv; + + /* + * Then, assign other fields in dev, using ether_setup() and some + * hand assignments + */ +// printk("usb_net_host: net_dev_init(nhs=%p)\n", nhs); + + /* + * Assign the hardware address of the board: use + * 40 00 00 00 00 XX, where XX is the USB address of the + * device + */ + net_dev->dev_addr[0] = 0x40; + net_dev->dev_addr[1] = 0; + net_dev->dev_addr[2] = 0; + net_dev->dev_addr[3] = 0; + net_dev->dev_addr[4] = 0xff; + net_dev->dev_addr[5] = 1; + + + nhs->client_addr[0] = 0x40; + nhs->client_addr[1] = 0; + nhs->client_addr[2] = 0; + nhs->client_addr[3] = 0; + nhs->client_addr[4] = 0; + nhs->client_addr[5] = 1; + + net_dev->open = net_host_open; + net_dev->stop = net_host_release; + net_dev->set_config = net_host_config; + net_dev->hard_start_xmit = net_host_tx; + net_dev->do_ioctl = net_host_ioctl; + net_dev->get_stats = net_host_stats; + + /* clear the statistics */ + ether_setup(net_dev); + + /* keep IP_PNP (ipconfig.c) from trying to use this interface */ + net_dev->flags &= ~IFF_BROADCAST; + + return 0; +} + +#if 0 +static ssize_t +net_host_write(const char * buffer, + size_t count) +{ + struct net_host_state *nhs = &static_net_host_state; + + char buf[64]; + + printk("USB_FILE_WRITE\n"); + + memset(buf, 0x0, 64); + nhs->writetransfer = usb_request_bulk(nhs->usb_dev, + nhs->writepipe, + net_host_write_irq, + buf, 32, nhs); + printk("bulk_msg \n"); + return -EINVAL; +} + +static ssize_t +read_net_host(const char * buffer, + size_t count) +{ + struct net_host_state *nhs = &static_net_host_state; + unsigned long retval; + char buf[256]; + printk("USB_FILE_READ\n"); + + printk("reading:>%s<\n",buf); + nhs->usb_dev->bus->op->bulk_msg(nhs->usb_dev, + nhs->readpipe, + buf, + 256, + &retval); + printk("done:>%s %x %x<\n",buf, buf[0], buf[1]); + return 1; +} +#endif + +/* + * called in response to usb interrupt request to client/function device + * + * We may get called several times with several packet fragments. + */ +static void +net_host_rx_irq(struct urb *urb) +{ + struct net_host_state *nhs = urb->context; + unsigned char *data = nhs->readbuffer; + int len = urb->actual_length; + unsigned char *mac_header; + int idx; + + if (urb->status) { + /* reset some variables in readiness for next packet */ + if(nhs->recv_skb != NULL ) + { + dev_kfree_skb(nhs->recv_skb); + nhs->recv_skb = NULL; + } + nhs->pktlen = 0; + return; + } + + /* Sanity check */ + if (data == NULL || len <= 0) + { + printk("rx_irq:WARNING data = NULL || LEN <= 0\n"); + if(nhs->recv_skb != NULL ) + { + dev_kfree_skb(nhs->recv_skb); + nhs->recv_skb = NULL; + } + nhs->pktlen = 0; + return; + } + +#if 0 + /* ----- dump the raw RX buffer ----- */ + { + unsigned int i,rxlen; + rxlen = ( len > 40 ) ? 40 : len; + printk("RX:[%d]",len); + for(i=0; i < rxlen; i++) + printk("%02x:",*(data + i) ); + printk("\n"); + } + /* ----- dump the raw buffer ----- */ +#endif + + /* Is this the 1st fragment? If so then allocate a skbuf and get pktlen*/ + if ( !nhs->pktlen && (nhs->recv_skb == NULL) ) + { + nhs->recv_skb = dev_alloc_skb(1518); + /* want the ip packet aligned on a 16 byte boundary so + * reserve 12 bytes, 2 empty, and 10 for the address. + * 2 bytes are used for packet length on the USB. + */ + skb_reserve(nhs->recv_skb, 12); + /* TODO I have assumed that len is >=2 to do this */ + nhs->pktlen = (data[0] & 0xff); + nhs->pktlen += ( (data[1] & 0xff) << 8 ); + } + + + /* Check we have enough room to fit "len" octets in our skb */ + if (skb_tailroom(nhs->recv_skb) < len) + { + printk("rx_irq: (PANIC) no room skb %p length %d copy %d tailroom %d\n", + nhs->recv_skb, nhs->recv_skb->len, len, skb_tailroom(nhs->recv_skb)); + /* If we dont have room then PANIC no point in going on */ + if(nhs->recv_skb != NULL ) + { + dev_kfree_skb(nhs->recv_skb); + nhs->recv_skb = NULL; + } + nhs->pktlen = 0; + return; + } + + + /* copy our fragment into socket buffer */ + memcpy(skb_put(nhs->recv_skb, len), data, len); + +#if 0 + printk("RX:pktlen=%d skb->len=%d\n",nhs->pktlen,nhs->recv_skb->len); +#endif + + /* TODO TODO packet size shoudln't be hard coded here XXX */ +#if 0 + printk("max pkt size %d\n", usb_maxpacket(nhs->usb_dev, nhs->readpipe, 0)); +#endif + + /* TODO TODO we need a case where skb->len > pktlen */ + + /* Check if we have received all packet fragments */ + if ( nhs->recv_skb->len == nhs->pktlen ) + { +#if 0 + printk("rx_irq:finished receiving len=%d\n",nhs->recv_skb->len); +#endif + /* finished receiving. now process packet */ +#ifdef V22 + if (nhs->net_dev->start) +#else + if (netif_running(nhs->net_dev)) +#endif + { + /* put the mac addresses back in, over the top of the + * size that was sent over. + * CF TODO Re-word above + * CF TODO Too many magic numbers. + * CF TODO Instead of using 10 use a number linked to + * CF TODO Ethel Allen for example in the _tx function + * CF TODO send_len = skb->len - 10; and ... + * CF TODO memcpy(nhs->writebuffer, (skb->data+10), send_len); + * CF TODO 10 should be replaced with (2 * ETH_ALEN) - LEN_SIZE + * CF TODO where LEN_SIZE=2 + */ + mac_header = skb_push(nhs->recv_skb, (2*ETH_ALEN)-2); + for (idx = 0; idx < ETH_ALEN; idx++) { + mac_header[idx] = nhs->net_dev->dev_addr[idx]; + mac_header[idx+ETH_ALEN] = nhs->client_addr[idx]; + //printk("%02x:%02x\n",mac_header[idx],mac_header[idx+ETH_ALEN]); + } + nhs->recv_skb->dev = nhs->net_dev; + nhs->recv_skb->protocol = eth_type_trans(nhs->recv_skb, + nhs->net_dev); + nhs->recv_skb->ip_summed = CHECKSUM_UNNECESSARY; + + nhs->eth_stats.rx_packets++; + nhs->eth_stats.rx_bytes += nhs->recv_skb->len; + + /* finally pass our skb upto protocol stack layers */ + netif_rx(nhs->recv_skb); + } + else + { + printk("rx_irq:WARNING net stopped\n"); + nhs->eth_stats.rx_dropped++; + if(nhs->recv_skb != NULL ) + dev_kfree_skb(nhs->recv_skb); + } + /* reset some variables in readiness for next packet */ + nhs->recv_skb = NULL; + nhs->pktlen = 0; + } +} + +static void * +net_host_probe(struct usb_device *usb_dev, unsigned int ifnum) +{ + struct net_host_state *nhs; + int cfgnum; + void *retval= NULL; + unsigned int pipe; + int maxp, ret; + +// printk("net_host_probe() usb_dev=%p ifnum=%d\n", usb_dev, ifnum); + + /* With the net host all we care about is the vendor ID + * and product ID. After that we know it's an net host + * and don't need to check the configuration. + */ + if (usb_dev->descriptor.idVendor != ID_VENDOR || + usb_dev->descriptor.idProduct != ID_PRODUCT) + { +#if 0 + printk("usb_net_host: ignoring device %x %x\n", + usb_dev->descriptor.idVendor, + usb_dev->descriptor.idProduct); +#endif + return (void *)0; + } + + /* have got an net host, set configuration 0. */ + printk("usb_net_host: found device\n"); + + usb_set_configuration(usb_dev, usb_dev->config[0].bConfigurationValue); + +#if 0 + nhs = kmalloc(sizeof(struct net_host_state), GFP_KERNEL); + if (nhs == NULL) { + return (void *)0; + } + memset((char *)nhs, 0, sizeof(struct net_host_state)); + + nhs->next = net_host_list; + net_host_list = nhs; +#else + nhs = &static_net_host_state; +#endif + + nhs->usb_dev = usb_dev; + + /* now find which endpoint is which */ + for (cfgnum = 0; + cfgnum < usb_dev->config[0].interface[0].altsetting[0].bNumEndpoints; + cfgnum++) + { + struct usb_endpoint_descriptor *ep; + + ep = &usb_dev->config[0].interface[0].altsetting[0].endpoint[cfgnum]; + + switch (ep->bEndpointAddress) { + case 0x01 : + /* this is out write endpoint */ + nhs->writeendp = ep->bEndpointAddress; + nhs->writepipe = usb_sndbulkpipe(usb_dev, nhs->writeendp); + nhs->writebuffer = (char *)__get_dma_pages(GFP_KERNEL, 0); + break; + case 0x82 : + nhs->readendp = ep->bEndpointAddress; + nhs->readpipe = usb_rcvbulkpipe(usb_dev, nhs->readendp); + nhs->read_interval = ep->bInterval; + nhs->readbuffer = (char *)__get_dma_pages(GFP_KERNEL, 0); + break; + default: + break; + } + } + +// printk("readendp %x writeendp %x\n", nhs->readendp, nhs->writeendp); + + nhs->present = 1; + memset(&nhs->ctrl_task, 0, sizeof(struct tq_struct)); + nhs->ctrl_task.sync = 0; + + /* allocate and setup an URB for continuous interrupt request */ + nhs->int_urb = usb_alloc_urb(0); + if (!nhs->int_urb) { + printk("usb_net_host: couldn't allocate interrupt urb\n"); + return (void *)0; + } + + // xxx use nhs->readpipe? it's set for bulk... :-( + + pipe = usb_rcvintpipe(usb_dev, nhs->readendp); + maxp = usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe)); + + FILL_INT_URB(nhs->int_urb, usb_dev, pipe, nhs->readbuffer, maxp, + net_host_rx_irq, nhs, nhs->read_interval); + + ret = usb_submit_urb(nhs->int_urb); + if (ret) { + printk("usb_net_host: usb_submit_urb failed (%d)\n", ret); + return (void *)0; + } + + retval = nhs; + + return retval; +} + +static void +net_host_disconnect(struct usb_device *usb_dev, void *ptr) +{ + struct net_host_state *nhs = ptr; + +// printk("net_host_disconnect(nhs=%p)\n", nhs); + + /* this might need work */ + nhs->present = 0; + +#ifdef V22 + if (nhs->net_dev) + nhs->net_dev->tbusy = 0; +#endif + + /* cancel any transactions */ + if (nhs->bulk_urb) { + usb_unlink_urb(nhs->bulk_urb); + usb_free_urb(nhs->bulk_urb); + nhs->bulk_urb = NULL; + } + + if (nhs->int_urb) { + usb_unlink_urb(nhs->int_urb); + usb_free_urb(nhs->int_urb); + nhs->int_urb = NULL; + } +} + +static struct usb_driver net_host_driver = { + name: "net_host", + probe: net_host_probe, + disconnect: net_host_disconnect, +}; + +static int __init +usb_net_host_init(void) +{ + int retval = 0; + struct net_device *net_dev; + struct net_host_state *nhs = &static_net_host_state; + +// printk("usb_net_host_init()\n"); + net_dev = kmalloc(sizeof(struct net_device), GFP_KERNEL); + + if (net_dev == NULL) + return -ENODEV; + + /* initialise USB */ + memset(nhs, 0, sizeof(struct net_host_state)); + nhs->present = 0; + nhs->net_dev = net_dev; + + usb_register(&net_host_driver); + + /* initialise ethernet style interface */ + memset((char *)net_dev, 0, sizeof(struct net_device)); + net_dev->next = NULL; + net_dev->base_addr = (__u32) 0; +#ifdef V22 + net_dev->name = "usb0"; +#else + strcpy(net_dev->name, "usb0"); +#endif + net_dev->irq = 0; + net_dev->init = net_dev_init; + net_dev->priv = nhs; + + if ((retval = register_netdev(net_dev))) + { + printk("usb_net_host: error %i registering net_device \"%s\"\n", + retval, net_dev->name); + } + + printk(KERN_INFO "usb_net_host: USB net host registered.\n"); + return 0; +} + +void __exit +usb_net_host_cleanup(void) +{ + struct net_host_state *nhs = &static_net_host_state; + +// printk("usb_net_host_cleanup()\n"); + + /* this, too, probably needs work */ + if (nhs->net_dev) + { + unregister_netdev(nhs->net_dev); + kfree(nhs->net_dev); + nhs->net_dev = NULL; + } + usb_deregister(&net_host_driver); +} + +/* not really, but I'll field email */ +MODULE_AUTHOR ("Brad Parker, brad@parker.boston.ma.us"); +MODULE_DESCRIPTION ("Net-host USB Interface Driver for Linux"); + +module_init(usb_net_host_init); +module_exit(usb_net_host_cleanup); + +