Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- diff -Naurp a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c
- --- a/arch/arm/mach-msm/devices_htc.c 2009-12-18 21:59:45.278473602 +0100
- +++ b/arch/arm/mach-msm/devices_htc.c 2009-12-18 21:59:31.147476769 +0100
- @@ -25,8 +25,14 @@
- #include <mach/board.h>
- #include <mach/board_htc.h>
- #include <mach/msm_hsusb.h>
- +
- +#ifdef CONFIG_USB_FUNCTION
- +#include <../../../drivers/usb/function/usb_function.h>
- #include <linux/usb/mass_storage_function.h>
- +#endif
- +#ifdef CONFIG_USB_ANDROID
- #include <linux/usb/android.h>
- +#endif
- #include <asm/mach/flash.h>
- #include <asm/setup.h>
- @@ -87,30 +93,121 @@ static int hsusb_phy_init_seq[] = { 0x40
- #ifdef CONFIG_USB_FUNCTION
- static char *usb_functions[] = {
- +#if defined(CONFIG_USB_FUNCTION_RNDIS_WCEIS)
- + /* ether *MUST* be first for Windows to detect it using Compatible IDs */
- + "ether",
- +#endif
- #if defined(CONFIG_USB_FUNCTION_MASS_STORAGE) || defined(CONFIG_USB_FUNCTION_UMS)
- "usb_mass_storage",
- #endif
- #ifdef CONFIG_USB_FUNCTION_ADB
- "adb",
- #endif
- +#if defined(CONFIG_USB_FUNCTION_FSYNC)
- + "fsync",
- +#endif
- +#if defined(CONFIG_USB_FUNCTION_DIAG)
- + "diag",
- +#endif
- +#if defined(CONFIG_USB_FUNCTION_SERIAL)
- + "fserial",
- +#endif
- +#if defined(CONFIG_USB_FUNCTION_PROJECTOR)
- + "projector",
- +#endif
- +#if defined(CONFIG_USB_FUNCTION_MTP_TUNNEL)
- + "mtp_tunnel",
- +#endif
- +#if defined(CONFIG_USB_FUNCTION_ETHER) && !defined(CONFIG_USB_FUNCTION_RNDIS_WCEIS)
- + "ether",
- +#endif
- };
- +/* The first product_id with all the functions required for the current setup
- + * will be used, so be careful about your ordering.
- + */
- static struct msm_hsusb_product usb_products[] = {
- +#if defined(CONFIG_USB_FUNCTION_MASS_STORAGE) || defined(CONFIG_USB_FUNCTION_UMS)
- {
- .product_id = 0x0c01,
- - .functions = 0x00000001, /* usb_mass_storage */
- + .functions = BIT(USB_FUNCTION_MASS_STORAGE_NUM),
- },
- +#endif
- +#if defined(CONFIG_USB_FUNCTION_ADB)
- {
- .product_id = 0x0c02,
- - .functions = 0x00000003, /* usb_mass_storage + adb */
- + .functions = BIT(USB_FUNCTION_MASS_STORAGE_NUM)
- + | BIT(USB_FUNCTION_ADB_NUM)
- + },
- +#endif
- +#if defined(CONFIG_USB_FUNCTION_FSERIAL)
- + {
- + .product_id = 0x0c03,
- + .functions = BIT(USB_FUNCTION_MASS_STORAGE_NUM)
- + | BIT(USB_FUNCTION_FSERIAL_NUM)
- + },
- +#endif
- +#if defined(CONFIG_USB_FUNCTION_PROJECTOR)
- + {
- + .product_id = 0x0c05,
- + .functions = BIT(USB_FUNCTION_MASS_STORAGE_NUM)
- + | BIT(USB_FUNCTION_PROJECTOR_NUM)
- },
- +#endif
- +#if defined(CONFIG_USB_FUNCTION_DIAG)
- + {
- + .product_id = 0x0c08,
- + .functions = BIT(USB_FUNCTION_MASS_STORAGE_NUM)
- + | BIT(USB_FUNCTION_DIAG_NUM)
- + },
- +#endif
- +#if defined(CONFIG_USB_FUNCTION_FSERIAL) && defined(CONFIG_USB_FUNCTION_ADB)
- + {
- + .product_id = 0x0c04,
- + .functions = BIT(USB_FUNCTION_MASS_STORAGE_NUM)
- + | BIT(USB_FUNCTION_ADB_NUM)
- + | BIT(USB_FUNCTION_FSERIAL_NUM)
- + },
- +#endif
- +#if defined(CONFIG_USB_FUNCTION_PROJECTOR) && defined(CONFIG_USB_FUNCTION_ADB)
- + {
- + .product_id = 0x0c06,
- + .functions = BIT(USB_FUNCTION_MASS_STORAGE_NUM)
- + | BIT(USB_FUNCTION_ADB_NUM)
- + | BIT(USB_FUNCTION_PROJECTOR_NUM)
- + },
- +#endif
- +#if defined(CONFIG_USB_FUNCTION_DIAG) && defined(CONFIG_USB_FUNCTION_ADB)
- + {
- + .product_id = 0x0c07,
- + .functions = BIT(USB_FUNCTION_MASS_STORAGE_NUM)
- + | BIT(USB_FUNCTION_ADB_NUM)
- + | BIT(USB_FUNCTION_DIAG_NUM)
- + },
- +#endif
- +#if defined(CONFIG_USB_FUNCTION_ETHER)
- + {
- + .product_id = 0x0FFE,
- + .functions = BIT(USB_FUNCTION_INTERNET_SHARING_NUM)
- + },
- +#endif
- +#if defined(CONFIG_USB_FUNCTION_ETHER) && defined(CONFIG_USB_FUNCTION_RNDIS_WCEIS)
- + {
- + .product_id = 0x0FFE,
- + .functions = BIT(USB_FUNCTION_MASS_STORAGE_NUM)
- + | BIT(USB_FUNCTION_ADB_NUM)
- + | BIT(USB_FUNCTION_INTERNET_SHARING_NUM)
- + },
- +#endif
- };
- #endif
- struct msm_hsusb_platform_data msm_hsusb_pdata = {
- .phy_reset = internal_phy_reset,
- .phy_init_seq = hsusb_phy_init_seq,
- +#ifdef CONFIG_TROUT_BATTCHG
- .usb_connected = notify_usb_connected,
- +#endif
- #ifdef CONFIG_USB_FUNCTION
- .vendor_id = 0x0bb4,
- .product_id = 0x0c02,
- diff -Naurp a/drivers/usb/function/adb.c b/drivers/usb/function/adb.c
- --- a/drivers/usb/function/adb.c 2009-12-18 22:07:48.387475809 +0100
- +++ b/drivers/usb/function/adb.c 2009-12-18 22:07:57.603472988 +0100
- @@ -481,6 +481,7 @@ static struct usb_function usb_func_adb
- .ifc_ept_type = { EPT_BULK_OUT, EPT_BULK_IN },
- /* the adb function is only enabled when its driver file is open */
- + .position_bit = USB_FUNCTION_ADB_NUM,
- .disabled = 1,
- };
- diff -Naurp a/drivers/usb/function/diag.c b/drivers/usb/function/diag.c
- --- a/drivers/usb/function/diag.c 2009-12-18 22:08:17.379477528 +0100
- +++ b/drivers/usb/function/diag.c 2009-12-18 22:08:24.579489302 +0100
- @@ -231,6 +231,8 @@ static struct usb_function usb_func_diag
- .ifc_ept_count = 2,
- .ifc_ept_type = { EPT_BULK_OUT, EPT_BULK_IN },
- +
- + .position_bit = USB_FUNCTION_DIAG_NUM,
- };
- static int msm_diag_probe(struct platform_device *pdev)
- diff -Naurp a/drivers/usb/function/ether.c b/drivers/usb/function/ether.c
- --- a/drivers/usb/function/ether.c 2009-12-18 22:08:40.803488056 +0100
- +++ b/drivers/usb/function/ether.c 2009-12-18 22:08:46.615475831 +0100
- @@ -1,69 +1,396 @@
- /* drivers/usb/function/ether.c
- *
- - * Simple Ethernet Function Device
- + * RNDIS function device
- *
- - * Copyright (C) 2008 Google, Inc.
- - * Author: Brian Swetland <swetland@google.com>
- + * Copyright (C) 2008 htc, Inc.
- + * Author: Arec Kao <arec_kao@htc.com>
- *
- - * This software is licensed under the terms of the GNU General Public
- - * License version 2, as published by the Free Software Foundation, and
- - * may be copied, distributed, and modified under those terms.
- - *
- - * This program is distributed in the hope that it will be useful,
- - * but WITHOUT ANY WARRANTY; without even the implied warranty of
- - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- - * GNU General Public License for more details.
- - *
- - * Implements the "cdc_subset" bulk-only protocol supported by Linux.
- + * Based heavily on the function ethernet and gadget ethernet driver in
- + * android/drivers/usb/function/ether.c
- + * linux/drivers/usb/gadget/ether.c
- *
- */
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- +#include <linux/platform_device.h>
- #include <linux/netdevice.h>
- #include <linux/etherdevice.h>
- #include <linux/skbuff.h>
- +#include <linux/usb/ch9.h>
- +#include <linux/usb/cdc.h>
- #include "usb_function.h"
- +#include "rndis.h"
- /* Ethernet frame is 1514 + FCS, but round up to 512 * 3 so we
- * always queue a multiple of the USB max packet size (64 or 512)
- */
- -#define USB_MTU 1536
- +/* daniel, enlarget the size to 1600 to receive big size packet.
- + * should the size change to 2048(512*4)? need to confirm with USB team
- + */
- +#define USB_MTU 1600//1536
- #define MAX_TX 8
- #define MAX_RX 8
- +#define MAX_INTR_TX 4
- +#define MAX_EP0_RX 2
- +
- +#define DEBUG
- +#ifdef DEBUG
- +#define DBG(fmt,args...) \
- + printk(KERN_DEBUG fmt, ## args)
- +#else
- +#define DBG(fmt,args...) \
- + do { } while (0)
- +#endif /* DEBUG */
- +
- +#define ETHER_FUNCTION_NAME "ether"
- +
- +static char manufacturer [10] = "HTC";
- +
- +#define STATUS_INTERVAL_MSEC 9 /* 2^8*125us = 32ms */
- +
- +#define HS_BPS (13 * 512 * 8 * 1000 * 8)
- +
- +static struct usb_interface_descriptor
- +control_intf = {
- + .bLength = USB_DT_INTERFACE_SIZE,
- + .bDescriptorType = USB_DT_INTERFACE,
- + //.bInterfaceNumber = 0,
- + .bAlternateSetting = 0,
- + .bNumEndpoints = 1,
- +#ifdef CONFIG_USB_FUNCTION_RNDIS_WCEIS
- + /* "Wireless" RNDIS; auto-detected by Windows */
- + .bInterfaceClass = USB_CLASS_WIRELESS_CONTROLLER,
- + .bInterfaceSubClass = 1,
- + .bInterfaceProtocol = 3,
- +#else
- + /* Standard, but not auto-detected by any Microsoft operating system. */
- + .bInterfaceClass = USB_CLASS_COMM,
- + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
- + .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,
- +#endif
- + .iInterface = STRING_ES,
- +};
- +
- +static struct usb_cdc_header_desc header_desc = {
- + .bLength = sizeof header_desc,
- + .bDescriptorType = USB_DT_CS_INTERFACE,
- + .bDescriptorSubType = USB_CDC_HEADER_TYPE,
- +
- + .bcdCDC = 0x0110,
- +};
- +
- +static struct usb_cdc_call_mgmt_descriptor call_mgmt_desc = {
- + .bLength = sizeof call_mgmt_desc,
- + .bDescriptorType = USB_DT_CS_INTERFACE,
- + .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
- +
- + .bmCapabilities = 0x00,
- + //.bDataInterface = 0x01,
- +};
- +
- +static struct usb_cdc_acm_descriptor acm_desc = {
- + .bLength = sizeof acm_desc,
- + .bDescriptorType = USB_DT_CS_INTERFACE,
- + .bDescriptorSubType = USB_CDC_ACM_TYPE,
- +
- + .bmCapabilities = 0x00,
- +};
- +
- +static struct usb_cdc_union_desc union_desc = {
- + .bLength = sizeof union_desc,
- + .bDescriptorType = USB_DT_CS_INTERFACE,
- + .bDescriptorSubType = USB_CDC_UNION_TYPE,
- +
- + //.bMasterInterface0 = 0, /* index of control interface */
- + //.bSlaveInterface0 = 1, /* index of DATA interface */
- +};
- +
- +static struct usb_endpoint_descriptor
- +status_desc = {
- + .bLength = USB_DT_ENDPOINT_SIZE,
- + .bDescriptorType = USB_DT_ENDPOINT,
- + //.bEndpointAddress = 0x81,
- + .bmAttributes = USB_ENDPOINT_XFER_INT,
- + .wMaxPacketSize = 64,
- + .bInterval = STATUS_INTERVAL_MSEC,
- +};
- +
- +static struct usb_interface_descriptor
- +data_intf = {
- + .bLength = sizeof data_intf,
- + .bDescriptorType = USB_DT_INTERFACE,
- +
- + //.bInterfaceNumber = 1,
- + .bAlternateSetting = 0,
- + .bNumEndpoints = 2,
- + .bInterfaceClass = USB_CLASS_CDC_DATA,
- + .bInterfaceSubClass = 0,
- + .bInterfaceProtocol = 0,
- + .iInterface = STRING_ES,
- +};
- +
- +//OUT
- +static struct usb_endpoint_descriptor
- +sink_desc = {
- + .bLength = USB_DT_ENDPOINT_SIZE,
- + .bDescriptorType = USB_DT_ENDPOINT,
- + //.bEndpointAddress = 0x01,
- + .bmAttributes = USB_ENDPOINT_XFER_BULK,
- + .wMaxPacketSize = 512,
- +};
- +
- +//IN
- +static struct usb_endpoint_descriptor
- +source_desc = {
- + .bLength = USB_DT_ENDPOINT_SIZE,
- + .bDescriptorType = USB_DT_ENDPOINT,
- + //.bEndpointAddress = 0x82,
- + .bmAttributes = USB_ENDPOINT_XFER_BULK,
- + .wMaxPacketSize = 512,
- +};
- +
- +static struct usb_descriptor_header *rndis_desc [] = {
- + (struct usb_descriptor_header *) &control_intf,
- + (struct usb_descriptor_header *) &header_desc,
- + (struct usb_descriptor_header *) &call_mgmt_desc,
- + (struct usb_descriptor_header *) &acm_desc,
- + (struct usb_descriptor_header *) &union_desc,
- + (struct usb_descriptor_header *) &status_desc,
- + (struct usb_descriptor_header *) &data_intf,
- + (struct usb_descriptor_header *) &sink_desc,
- + (struct usb_descriptor_header *) &source_desc,
- + NULL,
- +};
- struct ether_context {
- spinlock_t lock;
- struct net_device *dev;
- +
- + struct usb_endpoint *intr_in;
- struct usb_endpoint *out;
- struct usb_endpoint *in;
- + struct usb_endpoint *ep0in;
- + struct usb_endpoint *ep0out;
- +
- struct list_head rx_reqs;
- struct list_head tx_reqs;
- + struct list_head tx_intr_reqs;
- + struct list_head rx_cmd_reqs;
- struct net_device_stats stats;
- + u8 host_mac [ETH_ALEN];
- + int rndis_config;
- + u16 cdc_filter;
- + int registered;
- + int online;
- };
- +static void receive_rndis_command (struct usb_endpoint *ep, struct usb_request *req);
- +
- static int ether_queue_out(struct ether_context *ctxt,
- struct usb_request *req);
- static void ether_in_complete(struct usb_endpoint *ept,
- struct usb_request *req);
- static void ether_out_complete(struct usb_endpoint *ept,
- struct usb_request *req);
- +static void eth_bind(struct usb_endpoint **ept,
- + void *_ctxt);
- +static void eth_unbind(void *_ctxt);
- +
- +static void eth_configure(int configured, void *_ctxt);
- +
- +static int eth_setup(struct usb_ctrlrequest *ctrl, void *buf,
- + int len, void *_ctxt);
- +
- +static void eth_start (struct ether_context *ctxt, gfp_t gfp_flags);
- +
- +static void rx_fill(struct ether_context *ctxt);
- +
- +static size_t rndis_desc_copy(char *buf, size_t buf_len, int ifc_number);
- +
- +static struct usb_function usb_func_ether = {
- + .bind = eth_bind,
- + .unbind = eth_unbind,
- + .configure = eth_configure,
- + .setup = eth_setup,
- +
- + .name = ETHER_FUNCTION_NAME,
- +
- + .ifc_class = 0x02,
- + .ifc_subclass = 0x02,
- + .ifc_protocol = 0xff,
- +
- + .ifc_name = "ether",
- +
- + .ifc_num = 2,
- + .ifc_ept_count = 3,
- + .ifc_ept_type = { EPT_INT_IN, EPT_BULK_OUT, EPT_BULK_IN },
- +
- + .ifc_copy = rndis_desc_copy,
- +
- + .position_bit = USB_FUNCTION_INTERNET_SHARING_NUM,
- + .disabled = 1,
- +};
- +
- +/* remove a request from the head of a list */
- +
- +static struct usb_request *req_get(struct ether_context *ctxt, struct list_head *head)
- +{
- + unsigned long flags;
- + struct usb_request *req;
- +
- + spin_lock_irqsave(&ctxt->lock, flags);
- + if (list_empty(head)) {
- + req = 0;
- + } else {
- + req = list_first_entry(head, struct usb_request, list);
- + list_del(&req->list);
- + }
- + spin_unlock_irqrestore(&ctxt->lock, flags);
- + return req;
- +}
- +
- +static void
- +rndis_control_ack_complete (struct usb_endpoint *ept, struct usb_request *req)
- +{
- + struct ether_context *ctxt = req->context;
- + unsigned long flags;
- +
- + //DBG("rndis_control_ack_complete\n");
- + if (req->status || req->actual != req->length)
- + printk(KERN_ERR "rndis control ack complete --> %d, %d/%d\n",
- + req->status, req->actual, req->length);
- + req->context = NULL;
- + spin_lock_irqsave(&ctxt->lock, flags);
- + list_add_tail(&req->list, &ctxt->tx_intr_reqs);
- + spin_unlock_irqrestore(&ctxt->lock, flags);
- +
- +}
- +
- +static int rndis_control_ack (struct net_device *net)
- +{
- + struct ether_context *ctxt = netdev_priv(net);
- + struct usb_request *resp = req_get(ctxt, &ctxt->tx_intr_reqs);
- + if (!resp) {
- + printk(KERN_ERR "rndis_control_ack: get request fial\n");
- + return -1;
- + }
- + resp->length = 8;
- + resp->context = ctxt;
- +
- + *((__le32 *) resp->buf) = __constant_cpu_to_le32 (1);
- + *((__le32 *) resp->buf + 1) = __constant_cpu_to_le32 (0);
- +
- + if (usb_ept_queue_xfer(ctxt->intr_in, resp)) {
- + resp->status = 0;
- + rndis_control_ack_complete (ctxt->intr_in, resp);
- + }
- +
- + return 0;
- +}
- +
- +static ssize_t store_enable(struct device *dev, struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- + unsigned long ul;
- + if (count > 2 || (buf[0] != '0' && buf[0] != '1')) {
- + printk(KERN_ERR "Can't enable/disable ether %s\n", buf);
- + return -EINVAL;
- + }
- + ul = simple_strtoul(buf, NULL, 10);
- + usb_function_enable(ETHER_FUNCTION_NAME, ul);
- +
- + return count;
- +}
- +
- +static ssize_t show_enable(struct device *dev, struct device_attribute *attr,
- + char *buf)
- +{
- + if (return_usb_function_enabled(ETHER_FUNCTION_NAME) > 0) {
- + buf[0] = '1';
- + buf[1] = '\n';
- + }
- + else {
- + buf[0] = '0';
- + buf[1] = '\n';
- + }
- + return 2;
- +
- +}
- +
- +static DEVICE_ATTR(enable, 0644, show_enable, store_enable);
- -static void ether_bind(struct usb_endpoint **ept, void *_ctxt)
- +static void eth_unbind(void *_ctxt)
- +{
- + struct ether_context *ctxt = _ctxt;
- + struct usb_request *req;
- +
- + while ((req = req_get(ctxt, &ctxt->tx_intr_reqs))) {
- + usb_ept_free_req(ctxt->intr_in, req);
- + }
- + while ((req = req_get(ctxt, &ctxt->rx_cmd_reqs))) {
- + usb_ept_free_req(ctxt->ep0out, req);
- + }
- + while ((req = req_get(ctxt, &ctxt->rx_reqs))) {
- + usb_ept_free_req(ctxt->out, req);
- + }
- + while ((req = req_get(ctxt, &ctxt->tx_reqs))) {
- + usb_ept_free_req(ctxt->in, req);
- + }
- + if (ctxt->registered == 1) {
- + device_remove_file(&ctxt->dev->dev, &dev_attr_enable);
- + ctxt->registered = 0;
- + }
- + rndis_deregister (ctxt->rndis_config);
- + //rndis_exit ();
- + //unregister_netdev (ctxt->dev);
- + //free_netdev(ctxt->dev);
- +}
- +
- +static void eth_bind(struct usb_endpoint **ept, void *_ctxt)
- {
- struct ether_context *ctxt = _ctxt;
- struct usb_request *req;
- unsigned long flags;
- int n;
- + int status = -ENOMEM;
- + struct usb_fi_ept *ept_info;
- +
- + u32 vendorID = 0x0bb4;
- +
- + ctxt->registered = 0;
- +
- + ctxt->intr_in = ept[0];
- + ctxt->out = ept[1];
- + ctxt->in = ept[2];
- + ctxt->ep0in = ept[3];
- + ctxt->ep0out = ept[4];
- - ctxt->out = ept[0];
- - ctxt->in = ept[1];
- + for (n = 0; n < MAX_INTR_TX; n++) {
- + req = usb_ept_alloc_req(ctxt->intr_in, 8);
- + if (!req)
- + break;
- + req->complete = rndis_control_ack_complete;
- + spin_lock_irqsave(&ctxt->lock, flags);
- + list_add_tail(&req->list, &ctxt->tx_intr_reqs);
- + spin_unlock_irqrestore(&ctxt->lock, flags);
- + }
- +
- + for (n = 0; n < MAX_EP0_RX; n++) {
- + req = usb_ept_alloc_req(ctxt->ep0out, 4096);
- + if (!req)
- + break;
- + req->complete = receive_rndis_command;
- + spin_lock_irqsave(&ctxt->lock, flags);
- + list_add_tail(&req->list, &ctxt->rx_cmd_reqs);
- + spin_unlock_irqrestore(&ctxt->lock, flags);
- + }
- for (n = 0; n < MAX_RX; n++) {
- req = usb_ept_alloc_req(ctxt->out, 0);
- @@ -83,6 +410,81 @@ static void ether_bind(struct usb_endpoi
- list_add_tail(&req->list, &ctxt->tx_reqs);
- spin_unlock_irqrestore(&ctxt->lock, flags);
- }
- +
- + ept_info = get_ept_info(ETHER_FUNCTION_NAME);
- + if (!ept_info)
- + goto fail;
- +
- + status_desc.bEndpointAddress = ept_info[0].desc.bEndpointAddress;
- + sink_desc.bEndpointAddress = ept_info[1].desc.bEndpointAddress;
- + source_desc.bEndpointAddress = ept_info[2].desc.bEndpointAddress;
- +
- + status = device_create_file(&ctxt->dev->dev, &dev_attr_enable);
- + if (status != 0) {
- + printk(KERN_ERR "ether device_create_file failed: %d\n", status);
- + goto fail;
- + }
- + ctxt->registered = 1;
- + ctxt->online = 0;
- + status = rndis_init();
- + if (status < 0) {
- + printk(KERN_ERR "can't init RNDIS, %d\n", status);
- + goto fail;
- + }
- +
- + netif_stop_queue (ctxt->dev);
- + netif_carrier_off (ctxt->dev);
- +
- + ctxt->rndis_config = rndis_register (rndis_control_ack);
- + if (ctxt->rndis_config < 0) {
- + printk(KERN_ERR "ether bind: rndis_register fial, %d\n", ctxt->rndis_config);
- + //unregister_netdev (ctxt->dev);
- + goto fail;
- + }
- +
- + rndis_set_host_mac (ctxt->rndis_config, ctxt->host_mac);
- + if (rndis_set_param_dev (ctxt->rndis_config, ctxt->dev,
- + &ctxt->stats, &ctxt->cdc_filter))
- + goto fail;
- + if (rndis_set_param_vendor(ctxt->rndis_config, vendorID,
- + manufacturer))
- + goto fail;
- + if (rndis_set_param_medium(ctxt->rndis_config,
- + NDIS_MEDIUM_802_3, 0))
- + goto fail;
- +
- + return;
- +
- +fail:
- + eth_unbind (ctxt);
- + return;
- +}
- +
- +static size_t rndis_desc_copy(char *buf, size_t buf_len, int ifc_number)
- +{
- + char *ptr = buf;
- + struct usb_descriptor_header **src = rndis_desc;
- +
- + control_intf.bInterfaceNumber = ifc_number;
- + data_intf.bInterfaceNumber = ifc_number + 1;
- +
- + call_mgmt_desc.bDataInterface = data_intf.bInterfaceNumber;
- + union_desc.bMasterInterface0 = control_intf.bInterfaceNumber;
- + union_desc.bSlaveInterface0 = data_intf.bInterfaceNumber;
- +
- + for (; NULL != *src; src++) {
- + unsigned len = (*src)->bLength;
- + if (unlikely(buf_len < (ptr - buf + len)))
- + goto bad_setup_size;
- + memcpy(ptr, *src, len);
- + ptr += len;
- + }
- +
- + return ptr - buf;
- +
- +bad_setup_size:
- + printk(KERN_ERR "ether: Increase SETUP_BUF_SIZE\n");
- + return 0;
- }
- static void ether_in_complete(struct usb_endpoint *ept,
- @@ -113,14 +515,24 @@ static void ether_out_complete(struct us
- {
- struct sk_buff *skb = req->context;
- struct ether_context *ctxt = *((void **) skb->cb);
- + int status = req->status;
- - if (req->status == 0) {
- + if (status == 0) {
- skb_put(skb, req->actual);
- + status = rndis_rm_hdr (skb);
- + if (status < 0
- + || ETH_HLEN > skb->len
- + || skb->len > ETH_FRAME_LEN) {
- + ctxt->stats.rx_length_errors++;
- + printk(KERN_ERR "rx length %d\n", skb->len);
- + goto error;
- + }
- skb->protocol = eth_type_trans(skb, ctxt->dev);
- ctxt->stats.rx_packets++;
- ctxt->stats.rx_bytes += req->actual;
- netif_rx(skb);
- } else {
- +error:
- dev_kfree_skb_any(skb);
- ctxt->stats.rx_errors++;
- }
- @@ -169,52 +581,148 @@ fail:
- return ret;
- }
- -static void ether_configure(int configured, void *_ctxt)
- +static void rx_fill(struct ether_context *ctxt)
- {
- - unsigned long flags;
- - struct ether_context *ctxt = _ctxt;
- struct usb_request *req;
- + /* we're online -- get all rx requests queued */
- + for (;;) {
- + req = req_get(ctxt, &ctxt->rx_reqs);
- + if (!req)
- + break;
- + if (ether_queue_out(ctxt, req))
- + break;
- + }
- +}
- - pr_info("ether_configure() %d\n", configured);
- +static void eth_configure(int configured, void *_ctxt)
- +{
- + struct ether_context *ctxt = _ctxt;
- + netif_stop_queue(ctxt->dev);
- + netif_carrier_off(ctxt->dev);
- + ctxt->online = 0;
- if (configured) {
- - /* we're online -- get all rx requests queued */
- - for (;;) {
- - spin_lock_irqsave(&ctxt->lock, flags);
- - if (list_empty(&ctxt->rx_reqs)) {
- - req = 0;
- - } else {
- - req = list_first_entry(&ctxt->rx_reqs,
- - struct usb_request,
- - list);
- - list_del(&req->list);
- - }
- - spin_unlock_irqrestore(&ctxt->lock, flags);
- - if (!req)
- - break;
- - if (ether_queue_out(ctxt, req))
- - break;
- - }
- + ctxt->online = 1;
- + rx_fill(ctxt);
- + netif_carrier_on(ctxt->dev);
- + if (netif_running(ctxt->dev))
- + eth_start(ctxt, GFP_KERNEL);
- } else {
- + rndis_uninit(ctxt->rndis_config);
- /* all pending requests will be canceled */
- }
- }
- -static struct usb_function usb_func_ether = {
- - .bind = ether_bind,
- - .configure = ether_configure,
- +/*
- +static void queue_command_request(struct usb_endpoint *ep, struct usb_request *req)
- +{
- + struct ether_context *ctxt = usb_func_ether.context;
- + unsigned long flags;
- - .name = "ether",
- + req->complete = 0;
- + spin_lock_irqsave(&ctxt->lock, flags);
- + list_add_tail(&req->list, &ctxt->rx_cmd_reqs);
- + spin_unlock_irqrestore(&ctxt->lock, flags);
- +}
- +*/
- - .ifc_class = 0x02,
- - .ifc_subclass = 0x0a,
- - .ifc_protocol = 0x00,
- +static void rndis_command_complete(struct usb_endpoint *ep, struct usb_request *req)
- +{
- + struct ether_context *ctxt = usb_func_ether.context;
- + unsigned long flags;
- + req->length = 0;
- + //req->complete = queue_command_request;
- + //usb_ept_queue_xfer(ctxt->ep0out, req);
- + req->complete = 0;
- + spin_lock_irqsave(&ctxt->lock, flags);
- + list_add_tail(&req->list, &ctxt->rx_cmd_reqs);
- + spin_unlock_irqrestore(&ctxt->lock, flags);
- - .ifc_name = "ether",
- +}
- - .ifc_ept_count = 2,
- - .ifc_ept_type = { EPT_BULK_OUT, EPT_BULK_IN },
- -};
- +static void receive_rndis_command (struct usb_endpoint *ep, struct usb_request *req)
- +{
- + struct ether_context *ctxt = usb_func_ether.context;
- + int status;
- +/*
- + DBG("receive_rndis_command: data %02x %02x %02x, actual length %d, req status %d\n", \
- + *((char *)(req->buf)), \
- + *((char *)(req->buf + 1)), \
- + *((char *)(req->buf + 2)), \
- + req->actual, \
- + req->status);
- +*/
- + /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
- + spin_lock(&ctxt->lock);
- + status = rndis_msg_parser (ctxt->rndis_config, (u8 *) req->buf);
- + if (status < 0)
- + printk(KERN_ERR "%s: rndis parse error %d\n", __FUNCTION__, status);
- + spin_unlock(&ctxt->lock);
- + req->length = 0;
- + req->complete = rndis_command_complete;
- + usb_ept_queue_xfer(ctxt->ep0in, req);
- +}
- +
- +
- +static int eth_setup(struct usb_ctrlrequest *ctrl, void *buf,
- + int len, void *_ctxt)
- +{
- + struct ether_context *ctxt = _ctxt;
- + int value = -EOPNOTSUPP;
- + u16 w_index = le16_to_cpu(ctrl->wIndex);
- + u16 w_value = le16_to_cpu(ctrl->wValue);
- + u16 w_length = le16_to_cpu(ctrl->wLength);
- +
- + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) {
- + switch (ctrl->bRequest) {
- + case USB_CDC_SEND_ENCAPSULATED_COMMAND:
- + {
- + struct usb_request *req;
- + if ( w_value || control_intf.bInterfaceNumber != w_index)
- + break;
- + req = req_get(ctxt, &ctxt->rx_cmd_reqs);
- + if (!req) {
- + printk(KERN_ERR "%s: could not obtain tx request\n", __FUNCTION__);
- + value = -ENOMEM;
- + break;
- + }
- + value = w_length;
- + req->length = w_length;
- + req->complete = receive_rndis_command;
- + usb_ept_queue_xfer(ctxt->ep0out, req);
- + break;
- + }
- + case USB_CDC_GET_ENCAPSULATED_RESPONSE:
- + if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)
- + == ctrl->bRequestType
- + && !w_value
- + && control_intf.bInterfaceNumber == w_index) {
- + u8 *tmp;
- + u32 n;
- +
- + /* return the result */
- + tmp = rndis_get_next_response(ctxt->rndis_config, &n);
- + if (tmp) {
- + memcpy(buf, tmp, n);
- + //req->complete = rndis_response_complete;
- + rndis_free_response(ctxt->rndis_config, tmp);
- + value = n;
- + }
- + /* else stalls ... spec says to avoid that */
- + }
- + break;
- + }
- + }
- +
- + if (value == -EOPNOTSUPP)
- + printk(KERN_ERR
- + "unknown class-specific control req "
- + "%02x.%02x v%04x i%04x l%u\n",
- + ctrl->bRequestType, ctrl->bRequest,
- + le16_to_cpu(ctrl->wValue), w_index, w_length);
- +
- + return value;
- +}
- static int usb_ether_xmit(struct sk_buff *skb, struct net_device *dev)
- {
- @@ -222,23 +730,26 @@ static int usb_ether_xmit(struct sk_buff
- struct usb_request *req;
- unsigned long flags;
- unsigned len;
- + struct sk_buff *skb_rndis;
- - spin_lock_irqsave(&ctxt->lock, flags);
- - if (list_empty(&ctxt->tx_reqs)) {
- - req = 0;
- - } else {
- - req = list_first_entry(&ctxt->tx_reqs,
- - struct usb_request, list);
- - list_del(&req->list);
- - if (list_empty(&ctxt->tx_reqs))
- - netif_stop_queue(dev);
- - }
- - spin_unlock_irqrestore(&ctxt->lock, flags);
- -
- + req = req_get(ctxt, &ctxt->tx_reqs);
- if (!req) {
- pr_err("usb_ether_xmit: could not obtain tx request\n");
- return 1;
- }
- + spin_lock_irqsave(&ctxt->lock, flags);
- + if (list_empty(&ctxt->tx_reqs))
- + netif_stop_queue(dev);
- + spin_unlock_irqrestore(&ctxt->lock, flags);
- +
- + skb_rndis = skb_realloc_headroom (skb,
- + sizeof (struct rndis_packet_msg_type));
- + if (!skb_rndis)
- + goto drop;
- +
- + dev_kfree_skb_any (skb);
- + skb = skb_rndis;
- + rndis_add_hdr (skb);
- /* ensure that we end with a short packet */
- len = skb->len;
- @@ -251,11 +762,12 @@ static int usb_ether_xmit(struct sk_buff
- req->length = len;
- if (usb_ept_queue_xfer(ctxt->in, req)) {
- +drop:
- spin_lock_irqsave(&ctxt->lock, flags);
- list_add_tail(&req->list, &ctxt->tx_reqs);
- - netif_start_queue(dev);
- + if (list_empty(&ctxt->tx_reqs))
- + netif_start_queue(dev);
- spin_unlock_irqrestore(&ctxt->lock, flags);
- -
- dev_kfree_skb_any(skb);
- ctxt->stats.tx_dropped++;
- @@ -265,13 +777,39 @@ static int usb_ether_xmit(struct sk_buff
- return 0;
- }
- +static void eth_start (struct ether_context *ctxt, gfp_t gfp_flags)
- +{
- +
- + /* fill the rx queue */
- + rx_fill (ctxt);
- +
- + /* and open the tx floodgates */
- + netif_wake_queue (ctxt->dev);
- + if (ctxt->online) {
- + rndis_set_param_medium (ctxt->rndis_config, NDIS_MEDIUM_802_3,
- + HS_BPS/100);
- + (void) rndis_signal_connect (ctxt->rndis_config);
- + }
- +}
- +
- static int usb_ether_open(struct net_device *dev)
- {
- + struct ether_context *ctxt = netdev_priv(dev);
- + DBG("usb_ether_open\n");
- + if (netif_carrier_ok (ctxt->dev))
- + eth_start (ctxt, GFP_KERNEL);
- return 0;
- }
- static int usb_ether_stop(struct net_device *dev)
- {
- + struct ether_context *ctxt = netdev_priv(dev);
- + DBG("usb_ether_stop\n");
- + netif_stop_queue (dev);
- + if (ctxt->online) {
- + rndis_set_param_medium(ctxt->rndis_config, NDIS_MEDIUM_802_3, 0);
- + (void) rndis_signal_disconnect (ctxt->rndis_config);
- + }
- return 0;
- }
- @@ -285,10 +823,10 @@ static void __init usb_ether_setup(struc
- {
- struct ether_context *ctxt = netdev_priv(dev);
- - pr_info("usb_ether_setup()\n");
- -
- INIT_LIST_HEAD(&ctxt->rx_reqs);
- INIT_LIST_HEAD(&ctxt->tx_reqs);
- + INIT_LIST_HEAD(&ctxt->tx_intr_reqs);
- + INIT_LIST_HEAD(&ctxt->rx_cmd_reqs);
- spin_lock_init(&ctxt->lock);
- ctxt->dev = dev;
- @@ -301,11 +839,13 @@ static void __init usb_ether_setup(struc
- ether_setup(dev);
- random_ether_addr(dev->dev_addr);
- + random_ether_addr(ctxt->host_mac);
- }
- static int __init ether_init(void)
- {
- struct net_device *dev;
- + struct ether_context *ctxt;
- int ret;
- dev = alloc_netdev(sizeof(struct ether_context),
- @@ -314,14 +854,23 @@ static int __init ether_init(void)
- return -ENOMEM;
- ret = register_netdev(dev);
- - if (ret) {
- - free_netdev(dev);
- - } else {
- - struct ether_context *ctxt = netdev_priv(dev);
- - usb_func_ether.context = ctxt;
- - usb_function_register(&usb_func_ether);
- - }
- + if (ret)
- + goto err_register_netdev;
- +
- + ctxt = netdev_priv(dev);
- + usb_func_ether.context = ctxt;
- + ret = usb_function_register(&usb_func_ether);
- + if (ret < 0)
- + goto err_register_function;
- + return ret;
- +
- +err_register_function:
- + unregister_netdev(dev);
- +err_register_netdev:
- + free_netdev(dev);
- +
- return ret;
- +
- }
- module_init(ether_init);
- diff -Naurp a/drivers/usb/function/fserial.c b/drivers/usb/function/fserial.c
- --- a/drivers/usb/function/fserial.c 1970-01-01 01:00:00.000000000 +0100
- +++ b/drivers/usb/function/fserial.c 2009-12-18 23:15:40.959185975 +0100
- @@ -0,0 +1,1666 @@
- +/* drivers/usb/function/fserial.c
- + *
- + * Function Driver for USB Serial
- + *
- + * Copyright (C) 2008 htc, Inc.
- + * Author: Arec Kao <arec_kao@htc.com>
- + *
- + * Based heavily on the serial gadget driver in
- + * drivers/usb/gadget/serial.c
- + *
- + */
- +
- +#include <linux/kernel.h>
- +#include <linux/utsname.h>
- +#include <linux/device.h>
- +#include <linux/tty.h>
- +#include <linux/tty_flip.h>
- +#include <linux/platform_device.h>
- +
- +#include <linux/spinlock.h>
- +#include <linux/wait.h>
- +#include <linux/list.h>
- +
- +#include <linux/usb/ch9.h>
- +#include <linux/usb/cdc.h>
- +#include <linux/usb_usual.h>
- +
- +#include "usb_function.h"
- +
- +/* Defines */
- +
- +#define FS_VERSION_STR "v2.2"
- +#define FS_LONG_NAME "HTC Function Serial Interface"
- +
- +#define FS_SHORT_NAME "fserial"
- +
- +#define FS_MAJOR 127
- +#define FS_MINOR_START 0
- +
- +#define FS_NUM_PORTS 16
- +#define FS_NO_CONFIG_ID 0
- +
- +#define FS_MAX_DESC_LEN 256
- +
- +#define FS_DEFAULT_READ_Q_SIZE 32
- +#define FS_DEFAULT_WRITE_Q_SIZE 32
- +
- +#define FS_DEFAULT_WRITE_BUF_SIZE 8192
- +
- +#define FS_CLOSE_TIMEOUT 15
- +
- +#ifdef CONFIG_BUILD_CIQ
- + #define FS_DEFAULT_DTE_RATE 115200
- +#else
- + #define FS_DEFAULT_DTE_RATE 9600
- +#endif
- +#define FS_DEFAULT_DATA_BITS 8
- +#define FS_DEFAULT_PARITY USB_CDC_NO_PARITY
- +#define FS_DEFAULT_CHAR_FORMAT USB_CDC_1_STOP_BITS
- +#define MAX_PKT 512
- +
- +/* debug settings */
- +#ifdef DEBUG
- +static int debug = 1;
- +#else
- +#define debug 0
- +#endif
- +
- +#define fs_debug(format, arg...) \
- + do { if (debug) printk(KERN_DEBUG format, ## arg); } while(0)
- +#define fs_debug_level(level, format, arg...) \
- + do { if (debug>=level) printk(KERN_DEBUG format, ## arg); } while(0)
- +
- +
- +/* Structures */
- +
- +struct userial_context;
- +
- +/* circular buffer */
- +struct fs_buf {
- + unsigned int buf_size;
- + char *buf_buf;
- + char *buf_get;
- + char *buf_put;
- +};
- +
- +
- +/* the port structure holds info for each port, one for each minor number */
- +struct fs_port {
- + struct userial_context *port_dev; /* pointer to device struct */
- + struct tty_struct *port_tty; /* pointer to tty struct */
- + spinlock_t port_lock;
- + int port_num;
- + int port_open_count;
- + int port_in_use; /* open/close in progress */
- + wait_queue_head_t port_write_wait;/* waiting to write */
- + struct fs_buf *port_write_buf;
- + struct usb_cdc_line_coding port_line_coding;
- + u16 port_handshake_bits;
- +};
- +
- +struct userial_context{
- +
- + spinlock_t dev_lock;
- + int dev_config; /* configuration number */
- + struct usb_endpoint *dev_out_ep;
- + struct usb_endpoint *dev_in_ep;
- +
- + struct list_head dev_write_list; /* list of write requests */
- + struct list_head dev_read_list;
- +
- + struct fs_port *dev_port[FS_NUM_PORTS]; /* the ports */
- + struct platform_device *pdev;
- + int registered;
- +};
- +static struct userial_context _context;
- +struct device fserial_dev;
- +
- +/* Functions */
- +
- +/* module */
- +static int __init fs_module_init(void);
- +static void __exit fs_module_exit(void);
- +
- +/* tty driver */
- +static int fs_open(struct tty_struct *tty, struct file *file);
- +static void fs_close(struct tty_struct *tty, struct file *file);
- +static int fs_write(struct tty_struct *tty,
- + const unsigned char *buf, int count);
- +static int fs_put_char(struct tty_struct *tty, unsigned char ch);
- +static void fs_flush_chars(struct tty_struct *tty);
- +static int fs_write_room(struct tty_struct *tty);
- +static int fs_chars_in_buffer(struct tty_struct *tty);
- +static void fs_throttle(struct tty_struct * tty);
- +static void fs_unthrottle(struct tty_struct * tty);
- +static int fs_break(struct tty_struct *tty, int break_state);
- +static int fs_ioctl(struct tty_struct *tty, struct file *file,
- + unsigned int cmd, unsigned long arg);
- +static void fs_set_termios(struct tty_struct *tty, struct ktermios *old);
- +
- +static int fs_send(void);
- +static int fs_send_packet(struct userial_context *dev, char *packet, unsigned int size);
- +static int fs_recv_packet(struct userial_context *dev, char *packet, unsigned int size);
- +static void fs_read_complete(struct usb_endpoint *ep, struct usb_request *req);
- +static void fs_write_complete(struct usb_endpoint *ep, struct usb_request *req);
- +
- +/* gadget driver */
- +static void fs_bind(struct usb_endpoint **ept, void *_ctxt);
- +static void fs_unbind(void *_ctxt);
- +static int fs_setup( struct usb_ctrlrequest *ctrl, void* buf, int len, void *_ctxt);
- +//static void fs_setup_complete(struct usb_endpoint *ep, struct usb_request *req);
- +//static void fs_setup_complete_set_line_coding(struct usb_endpoint *ep,
- +// struct usb_request *req);
- +//static void fs_disconnect(struct usb_gadget *gadget);
- +
- +static void fs_configure(int configure, void *context);
- +//static void fs_reset_config(struct userial_context* dev);
- +
- +static int fs_alloc_ports(struct userial_context *ctxt, gfp_t kmalloc_flags);
- +static void fs_free_ports(struct userial_context *ctxt);
- +
- +/* circular buffer */
- +static struct fs_buf *fs_buf_alloc(unsigned int size, gfp_t kmalloc_flags);
- +static void fs_buf_free(struct fs_buf *gb);
- +static void fs_buf_clear(struct fs_buf *gb);
- +static unsigned int fs_buf_data_avail(struct fs_buf *gb);
- +static unsigned int fs_buf_space_avail(struct fs_buf *gb);
- +static unsigned int fs_buf_put(struct fs_buf *gb, const char *buf,
- + unsigned int count);
- +static unsigned int fs_buf_get(struct fs_buf *gb, char *buf,
- + unsigned int count);
- +
- +/* external functions */
- +
- +/* Globals */
- +
- +static struct mutex fs_open_close_lock[FS_NUM_PORTS];
- +
- +static unsigned int read_q_size = FS_DEFAULT_READ_Q_SIZE;
- +static unsigned int write_q_size = FS_DEFAULT_WRITE_Q_SIZE;
- +
- +static unsigned int write_buf_size = FS_DEFAULT_WRITE_BUF_SIZE;
- +
- +
- +/* tty driver struct */
- +static const struct tty_operations fs_tty_ops = {
- + .open = fs_open,
- + .close = fs_close,
- + .write = fs_write,
- + .put_char = fs_put_char,
- + .flush_chars = fs_flush_chars,
- + .write_room = fs_write_room,
- + .ioctl = fs_ioctl,
- + .set_termios = fs_set_termios,
- + .throttle = fs_throttle,
- + .unthrottle = fs_unthrottle,
- + .break_ctl = fs_break,
- + .chars_in_buffer = fs_chars_in_buffer,
- +};
- +static struct tty_driver *fs_tty_driver;
- +
- +/* function driver struct */
- +static struct usb_function usb_func_userial = {
- +
- + .bind = fs_bind,
- + .unbind = fs_unbind,
- + .configure = fs_configure,
- + .setup = fs_setup,
- +
- + .name = FS_SHORT_NAME,
- + .context = &_context,
- +
- + .ifc_class = USB_CLASS_CDC_DATA,
- + .ifc_subclass = 0,
- + .ifc_protocol = 0,
- +
- + .ifc_name = FS_SHORT_NAME,
- +
- + .ifc_ept_count = 2,
- + .ifc_ept_type = { EPT_BULK_OUT, EPT_BULK_IN },
- +
- + .disabled = 1,
- + .position_bit = USB_FUNCTION_FSERIAL_NUM,
- + .ifc_num = 1,
- +};
- +
- +
- +/* Module */
- +
- +#ifdef DEBUG
- +module_param(debug, int, S_IRUGO|S_IWUSR);
- +MODULE_PARM_DESC(debug, "Enable debugging, 0=off, 1=on");
- +#endif
- +
- +module_param(read_q_size, uint, S_IRUGO);
- +MODULE_PARM_DESC(read_q_size, "Read request queue size, default=32");
- +
- +module_param(write_q_size, uint, S_IRUGO);
- +MODULE_PARM_DESC(write_q_size, "Write request queue size, default=32");
- +
- +module_param(write_buf_size, uint, S_IRUGO);
- +MODULE_PARM_DESC(write_buf_size, "Write buffer size, default=8192");
- +
- +
- +module_init(fs_module_init);
- +module_exit(fs_module_exit);
- +
- +static void fserial_dev_release (struct device *dev) {}
- +
- +static int fserial_probe (struct platform_device *pdev)
- +{
- + struct userial_context *ctxt = &_context;
- + ctxt->pdev = pdev;
- + return 0;
- +}
- +
- +static struct platform_driver fserial_driver = {
- + .probe = fserial_probe,
- + .driver = { .name = FS_SHORT_NAME, },
- +};
- +
- +static void fserial_release (struct device *dev) {}
- +
- +static struct platform_device fserial_device = {
- + .name = FS_SHORT_NAME,
- + .id = -1,
- + .dev = {
- + .release = fserial_release,
- + },
- +};
- +
- +static ssize_t store_fserial_enable(struct device *dev, struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- + unsigned long ul;
- + if ((buf[0] != '0' && buf[0] != '1') && buf[1] != '\n')
- + {
- + printk(KERN_WARNING "Can't enable/disable fserial %s\n", buf);
- + return -EINVAL;
- + }
- + ul = simple_strtoul(buf, NULL, 10);
- + usb_function_enable(FS_SHORT_NAME, ul);
- + return count;
- +}
- +
- +static ssize_t show_fserial_enable(struct device *dev, struct device_attribute *attr,
- + char *buf)
- +{
- + int rc;
- + rc = sprintf(buf, "%d", return_usb_function_enabled(FS_SHORT_NAME));
- + return rc;
- +
- +}
- +static DEVICE_ATTR(fserial_enable, 0644, show_fserial_enable, store_fserial_enable);
- +
- +
- +/*
- +* fs_module_init
- +*
- +* Register as a USB gadget driver and a tty driver.
- +*/
- +static int __init fs_module_init(void)
- +{
- + int i;
- + int retval;
- + retval = usb_function_register(&usb_func_userial);
- + if (retval) {
- + printk(KERN_ERR "fs_module_init: cannot register function driver, ret=%d\n", retval);
- + return retval;
- + }
- +
- + fs_tty_driver = alloc_tty_driver(FS_NUM_PORTS);
- + if (!fs_tty_driver)
- + return -ENOMEM;
- + fs_tty_driver->owner = THIS_MODULE;
- + fs_tty_driver->driver_name = FS_SHORT_NAME;
- + fs_tty_driver->name = "ttyfs";
- + fs_tty_driver->major = FS_MAJOR;
- + fs_tty_driver->minor_start = FS_MINOR_START;
- + fs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
- + fs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
- + fs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
- + fs_tty_driver->init_termios = tty_std_termios;
- + fs_tty_driver->init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;
- +#ifdef CONFIG_BUILD_CIQ
- + fs_tty_driver->init_termios.c_lflag = 0; // Disable ALL for CIQ CADeT
- + fs_tty_driver->init_termios.c_iflag = 0; // Disable ALL for CIQ CADeT
- + fs_tty_driver->init_termios.c_oflag = 0; // Disable ALL for CIQ CADeT
- +#endif
- +
- + tty_set_operations(fs_tty_driver, &fs_tty_ops);
- +
- + for (i=0; i < FS_NUM_PORTS; i++)
- + mutex_init(&fs_open_close_lock[i]);
- +
- + retval = tty_register_driver(fs_tty_driver);
- + if (retval) {
- + put_tty_driver(fs_tty_driver);
- + printk(KERN_ERR "fs_module_init: cannot register tty driver, ret=%d\n", retval);
- + return retval;
- + }
- +
- + retval = platform_driver_register (&fserial_driver);
- + if (retval < 0)
- + return retval;
- + retval = platform_device_register (&fserial_device);
- + if (retval < 0)
- + goto err_register_device;
- +
- + return 0;
- +
- +err_register_device:
- + platform_driver_unregister(&fserial_driver);
- + return retval;
- +
- +}
- +
- +/*
- +* fs_module_exit
- +*
- +* Unregister as a tty driver.
- +*/
- +static void __exit fs_module_exit(void)
- +{
- + tty_unregister_driver(fs_tty_driver);
- + put_tty_driver(fs_tty_driver);
- +}
- +
- +/* TTY Driver */
- +
- +/*
- + * fs_open
- + */
- +static int fs_open(struct tty_struct *tty, struct file *file)
- +{
- + int port_num;
- + unsigned long flags;
- + struct fs_port *port;
- + struct userial_context *ctxt;
- + struct fs_buf *buf;
- + struct mutex *mtx;
- + int ret;
- +
- + port_num = tty->index;
- +
- + fs_debug("fs_open: (%d,%p,%p)\n", port_num, tty, file);
- +
- + if (port_num < 0 || port_num >= FS_NUM_PORTS) {
- + printk(KERN_ERR "fs_open: (%d,%p,%p) invalid port number\n",
- + port_num, tty, file);
- + return -ENODEV;
- + }
- +
- + ctxt = &_context;
- +
- + if (ctxt == NULL) {
- + printk(KERN_ERR "fs_open: (%d,%p,%p) NULL device pointer\n",
- + port_num, tty, file);
- + return -ENODEV;
- + }
- +
- + mtx = &fs_open_close_lock[port_num];
- + if (mutex_lock_interruptible(mtx)) {
- + printk(KERN_ERR
- + "fs_open: (%d,%p,%p) interrupted waiting for mutex\n",
- + port_num, tty, file);
- + return -ERESTARTSYS;
- + }
- +
- + spin_lock_irqsave(&ctxt->dev_lock, flags);
- +
- + /*if (ctxt->dev_config == FS_NO_CONFIG_ID) {
- + printk(KERN_ERR
- + "fs_open: (%d,%p,%p) device is not connected\n",
- + port_num, tty, file);
- + ret = -ENODEV;
- + goto exit_unlock_dev;
- + }*/
- +
- + port = ctxt->dev_port[port_num];
- +
- + if (port == NULL) {
- + printk(KERN_ERR "fs_open: (%d,%p,%p) NULL port pointer\n",
- + port_num, tty, file);
- + ret = -ENODEV;
- + goto exit_unlock_dev;
- + }
- +
- + spin_lock(&port->port_lock);
- + spin_unlock(&ctxt->dev_lock);
- +
- + if (port->port_dev == NULL) {
- + printk(KERN_ERR "fs_open: (%d,%p,%p) port disconnected (1)\n",
- + port_num, tty, file);
- + ret = -EIO;
- + goto exit_unlock_port;
- + }
- +
- + if (port->port_open_count > 0) {
- + ++port->port_open_count;
- + fs_debug("fs_open: (%d,%p,%p) already open\n",
- + port_num, tty, file);
- + ret = 0;
- + goto exit_unlock_port;
- + }
- +
- + tty->driver_data = NULL;
- +
- + /* mark port as in use, we can drop port lock and sleep if necessary */
- + port->port_in_use = 1;
- +
- + /* allocate write buffer on first open */
- + if (port->port_write_buf == NULL) {
- + spin_unlock_irqrestore(&port->port_lock, flags);
- + buf = fs_buf_alloc(write_buf_size, GFP_KERNEL);
- + spin_lock_irqsave(&port->port_lock, flags);
- +
- + /* might have been disconnected while asleep, check */
- + if (port->port_dev == NULL) {
- + printk(KERN_ERR
- + "fs_open: (%d,%p,%p) port disconnected (2)\n",
- + port_num, tty, file);
- + port->port_in_use = 0;
- + ret = -EIO;
- + goto exit_unlock_port;
- + }
- +
- + if ((port->port_write_buf=buf) == NULL) {
- + printk(KERN_ERR "fs_open: (%d,%p,%p) cannot allocate port write buffer\n",
- + port_num, tty, file);
- + port->port_in_use = 0;
- + ret = -ENOMEM;
- + goto exit_unlock_port;
- + }
- +
- + }
- +
- + /* wait for carrier detect (not implemented) */
- +
- + /* might have been disconnected while asleep, check */
- + if (port->port_dev == NULL) {
- + printk(KERN_ERR "fs_open: (%d,%p,%p) port disconnected (3)\n",
- + port_num, tty, file);
- + port->port_in_use = 0;
- + ret = -EIO;
- + goto exit_unlock_port;
- + }
- +
- + tty->driver_data = port;
- + port->port_tty = tty;
- + port->port_open_count = 1;
- + port->port_in_use = 0;
- +
- + fs_debug("fs_open: (%d,%p,%p) completed\n", port_num, tty, file);
- +
- + ret = 0;
- +
- +exit_unlock_port:
- + spin_unlock_irqrestore(&port->port_lock, flags);
- + mutex_unlock(mtx);
- + return ret;
- +
- +exit_unlock_dev:
- + spin_unlock_irqrestore(&ctxt->dev_lock, flags);
- + mutex_unlock(mtx);
- + return ret;
- +
- +}
- +
- +/*
- + * fs_close
- + */
- +
- +#define FS_WRITE_FINISHED_EVENT_SAFELY(p) \
- +({ \
- + int cond; \
- + \
- + spin_lock_irq(&(p)->port_lock); \
- + cond = !(p)->port_dev || !fs_buf_data_avail((p)->port_write_buf); \
- + spin_unlock_irq(&(p)->port_lock); \
- + cond; \
- +})
- +
- +static void fs_close(struct tty_struct *tty, struct file *file)
- +{
- + struct fs_port *port = tty->driver_data;
- + struct mutex *mtx;
- +
- + if (port == NULL) {
- + printk(KERN_ERR "fs_close: NULL port pointer\n");
- + return;
- + }
- +
- + fs_debug("fs_close: (%d,%p,%p)\n", port->port_num, tty, file);
- +
- + mtx = &fs_open_close_lock[port->port_num];
- + mutex_lock(mtx);
- +
- + spin_lock_irq(&port->port_lock);
- +
- + if (port->port_open_count == 0) {
- + printk(KERN_ERR
- + "fs_close: (%d,%p,%p) port is already closed\n",
- + port->port_num, tty, file);
- + goto exit;
- + }
- +
- + if (port->port_open_count > 1) {
- + --port->port_open_count;
- + goto exit;
- + }
- +
- + /* free disconnected port on final close */
- + if (port->port_dev == NULL) {
- + kfree(port);
- + goto exit;
- + }
- +
- + /* mark port as closed but in use, we can drop port lock */
- + /* and sleep if necessary */
- + port->port_in_use = 1;
- + port->port_open_count = 0;
- +
- + /* wait for write buffer to drain, or */
- + /* at most FS_CLOSE_TIMEOUT seconds */
- + if (fs_buf_data_avail(port->port_write_buf) > 0) {
- + spin_unlock_irq(&port->port_lock);
- + wait_event_interruptible_timeout(port->port_write_wait,
- + FS_WRITE_FINISHED_EVENT_SAFELY(port),
- + FS_CLOSE_TIMEOUT * HZ);
- + spin_lock_irq(&port->port_lock);
- + }
- +
- + /* free disconnected port on final close */
- + /* (might have happened during the above sleep) */
- + if (port->port_dev == NULL) {
- + kfree(port);
- + goto exit;
- + }
- +
- + fs_buf_clear(port->port_write_buf);
- +
- + tty->driver_data = NULL;
- + port->port_tty = NULL;
- + port->port_in_use = 0;
- +
- + fs_debug("fs_close: (%d,%p,%p) completed\n",
- + port->port_num, tty, file);
- +
- +exit:
- + spin_unlock_irq(&port->port_lock);
- + mutex_unlock(mtx);
- +}
- +
- +/*
- + * fs_write
- + */
- +static int fs_write(struct tty_struct *tty, const unsigned char *buf, int count)
- +{
- + unsigned long flags;
- + struct fs_port *port = tty->driver_data;
- + int ret;
- +
- + if (port == NULL) {
- + printk(KERN_ERR "fs_write: NULL port pointer\n");
- + return -EIO;
- + }
- +
- + fs_debug("fs_write: (%d,%p) writing %d bytes\n", port->port_num, tty,
- + count);
- +
- + if (count == 0)
- + return 0;
- +
- + spin_lock_irqsave(&port->port_lock, flags);
- +
- + if (port->port_dev == NULL) {
- + printk(KERN_ERR "fs_write: (%d,%p) port is not connected\n",
- + port->port_num, tty);
- + ret = -EIO;
- + goto exit;
- + }
- +
- + if (port->port_open_count == 0) {
- + printk(KERN_ERR "fs_write: (%d,%p) port is closed\n",
- + port->port_num, tty);
- + ret = -EBADF;
- + goto exit;
- + }
- +
- + count = fs_buf_put(port->port_write_buf, buf, count);
- +
- + spin_unlock_irqrestore(&port->port_lock, flags);
- +
- + fs_send();
- +
- + fs_debug("fs_write: (%d,%p) wrote %d bytes\n", port->port_num, tty,
- + count);
- +
- + return count;
- +
- +exit:
- + spin_unlock_irqrestore(&port->port_lock, flags);
- + return ret;
- +}
- +
- +/*
- + * fs_put_char
- + */
- +static int fs_put_char(struct tty_struct *tty, unsigned char ch)
- +{
- + unsigned long flags;
- + int retval = 1;
- + struct fs_port *port = tty->driver_data;
- +
- + if (port == NULL) {
- + printk(KERN_ERR "fs_put_char: NULL port pointer\n");
- + return 0;
- + }
- +
- + fs_debug("fs_put_char: (%d,%p) char=0x%x, called from %p\n",
- + port->port_num, tty, ch, __builtin_return_address(0));
- +
- + spin_lock_irqsave(&port->port_lock, flags);
- +
- + if (port->port_dev == NULL) {
- + printk(KERN_ERR "fs_put_char: (%d,%p) port is not connected\n",
- + port->port_num, tty);
- + retval = 0;
- + goto exit;
- + }
- +
- + if (port->port_open_count == 0) {
- + printk(KERN_ERR "fs_put_char: (%d,%p) port is closed\n",
- + port->port_num, tty);
- + retval = 0;
- + goto exit;
- + }
- +
- + fs_buf_put(port->port_write_buf, &ch, 1);
- +
- +exit:
- + spin_unlock_irqrestore(&port->port_lock, flags);
- + return retval;
- +}
- +
- +/*
- + * fs_flush_chars
- + */
- +static void fs_flush_chars(struct tty_struct *tty)
- +{
- + unsigned long flags;
- + struct fs_port *port = tty->driver_data;
- +
- + if (port == NULL) {
- + printk(KERN_ERR "fs_flush_chars: NULL port pointer\n");
- + return;
- + }
- +
- + fs_debug("fs_flush_chars: (%d,%p)\n", port->port_num, tty);
- +
- + spin_lock_irqsave(&port->port_lock, flags);
- +
- + if (port->port_dev == NULL) {
- + printk(KERN_ERR
- + "fs_flush_chars: (%d,%p) port is not connected\n",
- + port->port_num, tty);
- + goto exit;
- + }
- +
- + if (port->port_open_count == 0) {
- + printk(KERN_ERR "fs_flush_chars: (%d,%p) port is closed\n",
- + port->port_num, tty);
- + goto exit;
- + }
- +
- + spin_unlock_irqrestore(&port->port_lock, flags);
- +
- + fs_send();
- +
- + return;
- +
- +exit:
- + spin_unlock_irqrestore(&port->port_lock, flags);
- +}
- +
- +/*
- + * fs_write_room
- + */
- +static int fs_write_room(struct tty_struct *tty)
- +{
- +
- + int room = 0;
- + unsigned long flags;
- + struct fs_port *port = tty->driver_data;
- +
- +
- + if (port == NULL)
- + return 0;
- +
- + spin_lock_irqsave(&port->port_lock, flags);
- +
- + if (port->port_dev != NULL && port->port_open_count > 0
- + && port->port_write_buf != NULL)
- + room = fs_buf_space_avail(port->port_write_buf);
- +
- + spin_unlock_irqrestore(&port->port_lock, flags);
- +
- + fs_debug("fs_write_room: (%d,%p) room=%d\n",
- + port->port_num, tty, room);
- +
- + return room;
- +}
- +
- +/*
- + * fs_chars_in_buffer
- + */
- +static int fs_chars_in_buffer(struct tty_struct *tty)
- +{
- + int chars = 0;
- + unsigned long flags;
- + struct fs_port *port = tty->driver_data;
- +
- + if (port == NULL)
- + return 0;
- +
- + spin_lock_irqsave(&port->port_lock, flags);
- +
- + if (port->port_dev != NULL && port->port_open_count > 0
- + && port->port_write_buf != NULL)
- + chars = fs_buf_data_avail(port->port_write_buf);
- +
- + spin_unlock_irqrestore(&port->port_lock, flags);
- +
- + fs_debug("fs_chars_in_buffer: (%d,%p) chars=%d\n",
- + port->port_num, tty, chars);
- +
- + return chars;
- +}
- +
- +/*
- + * fs_throttle
- + */
- +static void fs_throttle(struct tty_struct *tty)
- +{
- +}
- +
- +/*
- + * fs_unthrottle
- + */
- +static void fs_unthrottle(struct tty_struct *tty)
- +{
- +}
- +
- +/*
- + * fs_break
- + */
- +static int fs_break(struct tty_struct *tty, int break_state)
- +{
- + return 0;
- +}
- +
- +/*
- + * fs_ioctl
- + */
- +static int fs_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
- +{
- + struct fs_port *port = tty->driver_data;
- +
- + if (port == NULL) {
- + printk(KERN_ERR "fs_ioctl: NULL port pointer\n");
- + return -EIO;
- + }
- +
- + fs_debug("fs_ioctl: (%d,%p,%p) cmd=0x%4.4x, arg=%lu\n",
- + port->port_num, tty, file, cmd, arg);
- +
- + /* handle ioctls */
- +
- + /* could not handle ioctl */
- + return -ENOIOCTLCMD;
- +}
- +
- +/*
- + * fs_set_termios
- + */
- +static void fs_set_termios(struct tty_struct *tty, struct ktermios *old)
- +{
- +}
- +
- +/*
- +* fs_send
- +*
- +* This function finds available write requests, calls
- +* fs_send_packet to fill these packets with data, and
- +* continues until either there are no more write requests
- +* available or no more data to send. This function is
- +* run whenever data arrives or write requests are available.
- +*/
- +static int fs_send(void)
- +{
- + int ret,len;
- + unsigned long flags;
- + struct usb_endpoint *ep;
- + struct usb_request *req;
- + struct userial_context *ctxt = &_context;
- +
- + if (ctxt == NULL) {
- + printk(KERN_ERR "fs_send: NULL device pointer\n");
- + return -ENODEV;
- + }
- +
- + spin_lock_irqsave(&ctxt->dev_lock, flags);
- +
- + ep = ctxt->dev_in_ep;
- +
- + while(!list_empty(&ctxt->dev_write_list)) {
- +
- + req = list_first_entry(&ctxt->dev_write_list,
- + struct usb_request, list);
- + len = fs_send_packet(ctxt, req->buf, MAX_PKT);
- +
- + if (len > 0) {
- + fs_debug_level(3, "fs_send: len=%d, 0x%2.2x "
- + "0x%2.2x 0x%2.2x ...\n", len,
- + *((unsigned char *)req->buf),
- + *((unsigned char *)req->buf+1),
- + *((unsigned char *)req->buf+2));
- + list_del(&req->list);
- + req->length = len;
- + spin_unlock_irqrestore(&ctxt->dev_lock, flags);
- + if ((ret=usb_ept_queue_xfer(ep, req))<0) {
- + printk(KERN_ERR
- + "fs_send: cannot queue read request, ret=%d\n",
- + ret);
- + spin_lock_irqsave(&ctxt->dev_lock, flags);
- + break;
- + }
- + spin_lock_irqsave(&ctxt->dev_lock, flags);
- + } else {
- + break;
- + }
- +
- + }
- +
- + spin_unlock_irqrestore(&ctxt->dev_lock, flags);
- +
- + return 0;
- +}
- +
- +/*
- + * fs_send_packet
- + *
- + * If there is data to send, a packet is built in the given
- + * buffer and the size is returned. If there is no data to
- + * send, 0 is returned. If there is any error a negative
- + * error number is returned.
- + *
- + * Called during USB completion routine, on interrupt time.
- + *
- + * We assume that disconnect will not happen until all completion
- + * routines have completed, so we can assume that the dev_port
- + * array does not change during the lifetime of this function.
- + */
- +static int fs_send_packet(struct userial_context *ctxt, char *packet, unsigned int size)
- +{
- + unsigned int len;
- + struct fs_port *port;
- +
- + /* TEMPORARY -- only port 0 is supported right now */
- + port = ctxt->dev_port[0];
- +
- + if (port == NULL) {
- + printk(KERN_ERR
- + "fs_send_packet: port=%d, NULL port pointer\n",
- + 0);
- + return -EIO;
- + }
- +
- + spin_lock(&port->port_lock);
- +
- + len = fs_buf_data_avail(port->port_write_buf);
- + if (len < size)
- + size = len;
- +
- + if (size == 0)
- + goto exit;
- +
- + size = fs_buf_get(port->port_write_buf, packet, size);
- +
- + if (port->port_tty)
- + wake_up_interruptible(&port->port_tty->write_wait);
- +
- +exit:
- + spin_unlock(&port->port_lock);
- + return size;
- +}
- +
- +/*
- + * fs_recv_packet
- + *
- + * Called for each USB packet received. Reads the packet
- + * header and stuffs the data in the appropriate tty buffer.
- + * Returns 0 if successful, or a negative error number.
- + *
- + * Called during USB completion routine, on interrupt time.
- + *
- + * We assume that disconnect will not happen until all completion
- + * routines have completed, so we can assume that the dev_port
- + * array does not change during the lifetime of this function.
- + */
- +static int fs_recv_packet(struct userial_context *ctxt, char *packet, unsigned int size)
- +{
- + unsigned int len;
- + struct fs_port *port;
- + int ret;
- + struct tty_struct *tty;
- +
- + /* TEMPORARY -- only port 0 is supported right now */
- + port = ctxt->dev_port[0];
- +
- + if (port == NULL) {
- + printk(KERN_ERR "fs_recv_packet: port=%d, NULL port pointer\n",
- + port->port_num);
- + return -EIO;
- + }
- +
- + spin_lock(&port->port_lock);
- +
- + if (port->port_open_count == 0) {
- + printk(KERN_ERR "fs_recv_packet: port=%d, port is closed\n",
- + port->port_num);
- + ret = -EIO;
- + goto exit;
- + }
- +
- +
- + tty = port->port_tty;
- +
- + if (tty == NULL) {
- + printk(KERN_ERR "fs_recv_packet: port=%d, NULL tty pointer\n",
- + port->port_num);
- + ret = -EIO;
- + goto exit;
- + }
- +
- + if (port->port_tty->magic != TTY_MAGIC) {
- + printk(KERN_ERR "fs_recv_packet: port=%d, bad tty magic\n",
- + port->port_num);
- + ret = -EIO;
- + goto exit;
- + }
- +
- + len = tty_buffer_request_room(tty, size);
- + if (len > 0) {
- + tty_insert_flip_string(tty, packet, len);
- + tty_flip_buffer_push(port->port_tty);
- + wake_up_interruptible(&port->port_tty->read_wait);
- + }
- + ret = 0;
- +exit:
- + spin_unlock(&port->port_lock);
- + return ret;
- +}
- +
- +/*
- +* fs_read_complete
- +*/
- +static void fs_read_complete(struct usb_endpoint *ep, struct usb_request *req)
- +{
- + int ret;
- + struct userial_context *ctxt = &_context;
- + fs_debug_level(3, "fs_read complete data:%c %c %c.... len= %u\n", *((char *)req->buf),
- + *((char *)req->buf+1),
- + *((char *)req->buf+2),
- + req->actual);
- +
- + if (ctxt == NULL) {
- + printk(KERN_ERR "fs_read_complete: NULL device pointer\n");
- + return;
- + }
- +
- + switch(req->status) {
- + case 0:
- + /* normal completion */
- + fs_recv_packet(ctxt, req->buf, req->actual);
- +requeue:
- + req->length = MAX_PKT;
- + if ((ret=usb_ept_queue_xfer(ctxt->dev_out_ep, req))<0) {
- + printk(KERN_ERR
- + "fs_read_complete: cannot queue read request, ret=%d\n",
- + ret);
- + }
- + break;
- +
- + case -ESHUTDOWN:
- + /* disconnect */
- + fs_debug("fs_read_complete: shutdown\n");
- + //fs_free_req(ep, req);
- + usb_ept_free_req(ep, req);
- + break;
- + case -ENODEV:
- + {
- + unsigned long flags;
- + fs_debug_level(3, "fs_read_complete: nodev\n");
- + spin_lock_irqsave(&ctxt->dev_lock, flags);
- + list_add_tail(&req->list, &ctxt->dev_read_list);
- + spin_unlock_irqrestore(&ctxt->dev_lock, flags);
- + break;
- + }
- + default:
- + /* unexpected */
- + printk(KERN_ERR
- + "fs_read_complete: unexpected status error, status=%d\n",
- + req->status);
- + goto requeue;
- + break;
- + }
- +}
- +
- +/*
- +* fs_write_complete
- +*/
- +static void fs_write_complete(struct usb_endpoint *ep, struct usb_request *req)
- +{
- + struct userial_context *ctxt = &_context;
- + fs_debug_level(3, "fs_write_complete:%c %c %c len:%u\n", *((char *)req->buf),
- + *((char *)req->buf+1),
- + *((char *)req->buf+2),
- + req->actual);
- + if (ctxt == NULL) {
- + printk(KERN_ERR "fs_write_complete: NULL device pointer\n");
- + return;
- + }
- +
- + switch(req->status) {
- + case 0:
- + /* normal completion */
- +requeue:
- + if (req == NULL) {
- + printk(KERN_ERR
- + "fs_write_complete: NULL request pointer\n");
- + return;
- + }
- +
- + spin_lock(&ctxt->dev_lock);
- + list_add_tail(&req->list, &ctxt->dev_write_list);
- + spin_unlock(&ctxt->dev_lock);
- +
- + fs_send();
- +
- + break;
- +
- + case -ESHUTDOWN:
- + /* disconnect */
- + fs_debug("fs_write_complete: shutdown\n");
- + usb_ept_free_req(ep, req);
- + break;
- +
- + default:
- + printk(KERN_ERR
- + "fs_write_complete: unexpected status error, status=%d\n",
- + req->status);
- + goto requeue;
- + break;
- + }
- +}
- +
- +/* Function Driver */
- +
- +/*
- + * fs_bind
- + *
- + * Called on module load. Allocates and initializes the device
- + * structure and a control request.
- + */
- +static void fs_bind(struct usb_endpoint **ept, void *_ctxt)
- +{
- +
- + struct userial_context *ctxt = _ctxt;
- + struct usb_endpoint *ep;
- + struct usb_request *req;
- + int i, ret;
- +
- + ctxt->registered = 0;
- + ctxt->dev_out_ep = ept[0];
- + ctxt->dev_in_ep = ept[1];
- + spin_lock_init(&ctxt->dev_lock);
- + INIT_LIST_HEAD(&ctxt->dev_write_list);
- + INIT_LIST_HEAD(&ctxt->dev_read_list);
- + if (fs_alloc_ports(ctxt, GFP_KERNEL) != 0) {
- + printk(KERN_ERR "fs_bind: cannot allocate ports\n");
- + fs_unbind(ctxt);
- + goto fail;
- + }
- +
- + ep = ctxt->dev_out_ep;
- + for (i=0; i<read_q_size ; i++) {
- + if ((req=usb_ept_alloc_req(ep, 4096))) {
- + req->complete = fs_read_complete;
- + req->context = ctxt;
- + list_add_tail(&req->list, &ctxt->dev_read_list);
- + } else {
- + printk(KERN_ERR "fs_bind: cannot allocate read requests\n");
- + goto fail;
- + }
- + }
- +
- + /* allocate write requests, and put on free list */
- + ep = ctxt->dev_in_ep;
- + for (i=0; i<write_q_size; i++) {
- + if ((req=usb_ept_alloc_req(ep, 4096))) {
- + req->complete = fs_write_complete;
- + req->context = ctxt;
- + list_add_tail(&req->list, &ctxt->dev_write_list);
- + } else {
- + printk(KERN_ERR "fs_bind: cannot allocate write requests\n");
- + goto fail;
- + }
- + }
- +
- + fserial_dev.release = fserial_dev_release;
- + fserial_dev.parent = &ctxt->pdev->dev;
- + strcpy(fserial_dev.bus_id, "interface");
- +
- + ret = device_register(&fserial_dev);
- + if (ret != 0) {
- + printk(KERN_WARNING "fserial_dev failed to register device: %d\n", ret);
- + goto fail;
- + }
- + ret = device_create_file(&fserial_dev, &dev_attr_fserial_enable);
- + if (ret != 0) {
- + printk(KERN_WARNING "fserial_dev device_create_file failed: %d\n", ret);
- + device_unregister(&fserial_dev);
- + goto fail;
- + }
- + ctxt->registered = 1;
- +
- + return;
- +
- +fail:
- + printk(KERN_ERR "fs_bind() could not allocate requests\n");
- +}
- +
- +/*
- + * fs_unbind
- + *
- + * Called on module unload. Frees the control request and device
- + * structure.
- + */
- +static void fs_unbind(void *_ctxt)
- +{
- +
- + struct userial_context *ctxt = _ctxt;
- + /* read/write requests already freed, only control request remains */
- + if (ctxt != NULL){
- +
- + fs_free_ports(ctxt);
- + /* if (dev->dev_in_ep)
- + usb_ep_disable(dev->dev_in_ep);
- + if (dev->dev_out_ep)
- + usb_ep_disable(dev->dev_out_ep);
- + */
- + if (ctxt->registered) {
- + device_remove_file(&fserial_dev, &dev_attr_fserial_enable);
- + device_unregister(&fserial_dev);
- + ctxt->registered = 0;
- + }
- + }
- +}
- +
- +
- +
- +
- +
- +/*
- + * fs_setup
- + *
- + * Implements all the control endpoint functionality that's not
- + * handled in hardware or the hardware driver.
- + *
- + * Returns the size of the data sent to the host, or a negative
- + * error number.
- + */
- +static int fs_setup(struct usb_ctrlrequest *ctrl, void *buf, int len, void *_ctxt)
- +{
- + int ret = -EOPNOTSUPP;
- + struct userial_context * ctxt = _ctxt;
- + struct fs_port *port = ctxt->dev_port[0];
- + printk(KERN_ERR "fs_setup\n");
- +
- + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) {
- + if (ctrl->wIndex != 2)
- + return -1;
- + switch (ctrl->bRequest) {
- + case USB_CDC_REQ_SET_LINE_CODING:
- + //if (ctrl->wLength != sizeof(struct usb_cdc_line_coding))
- + // break;
- + //ret = ctrl->wLength;
- + //req->complete = fs_setup_complete_set_line_coding;//???????????????
- + printk(KERN_WARNING "fs_setup: set_line_coding unuspported\n");
- + break;
- +
- + case USB_CDC_REQ_GET_LINE_CODING:
- + ret = min(ctrl->wLength, (u16)sizeof(struct usb_cdc_line_coding));
- + if (port) {
- + spin_lock(&port->port_lock);
- + memcpy(buf, &port->port_line_coding, ret);
- + spin_unlock(&port->port_lock);
- + }
- + break;
- +
- + case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
- + if (ctrl->wLength != 0)
- + break;
- + ret = 0;
- + if (port) {
- + /* REVISIT: we currently just remember this data.
- + * If we change that, update whatever hardware needs
- + * updating.
- + */
- + spin_lock(&port->port_lock);
- + port->port_handshake_bits = ctrl->wValue;
- + spin_unlock(&port->port_lock);
- + }
- + break;
- +
- + default:
- + printk(KERN_ERR "fs_setup: unknown class request, "
- + "type=%02x, request=%02x, value=%04x, "
- + "index=%04x, length=%d\n",
- + ctrl->bRequestType, ctrl->bRequest,
- + ctrl->wValue, ctrl->wIndex, ctrl->wLength);
- + break;
- + }
- + }
- + else
- + printk(KERN_ERR "fs_setup: unknown request, "
- + "type=%02x, request=%02x, value=%04x, "
- + "index=%04x, length=%d\n",
- + ctrl->bRequestType, ctrl->bRequest,
- + ctrl->wValue, ctrl->wIndex, ctrl->wLength);
- + return ret;
- +
- +}
- +
- +#if 0
- +static void fs_setup_complete_set_line_coding(struct usb_endpoint *ep,
- + struct usb_request *req)
- +{
- + struct fs_port *port = _context.dev_port[0]; /* ACM only has one port */
- +
- + switch (req->status) {
- + case 0:
- + /* normal completion */
- + if (req->actual != sizeof(port->port_line_coding))
- +// usb_ep_set_halt(ep); ???????????????????
- +// ep0_stall();
- + printk(KERN_ERR "set_complete_set_line_coding error");
- + else if (port) {
- + struct usb_cdc_line_coding *value = req->buf;
- +
- + /* REVISIT: we currently just remember this data.
- + * If we change that, (a) validate it first, then
- + * (b) update whatever hardware needs updating.
- + */
- + spin_lock(&port->port_lock);
- + port->port_line_coding = *value;
- + spin_unlock(&port->port_lock);
- + }
- + break;
- +
- + case -ESHUTDOWN:
- + /* disconnect */
- + usb_ept_free_req(ep, req);
- + break;
- +
- + default:
- + /* unexpected */
- + break;
- + }
- + return;
- +}
- +#endif
- +
- +
- +/*
- + * fs_disconnect
- + *
- + * Called when the device is disconnected. Frees the closed
- + * ports and disconnects open ports. Open ports will be freed
- + * on close. Then reallocates the ports for the next connection.
- + */
- +
- +
- +//move disconnect function to fs_close??????
- +static void fs_disconnect(struct userial_context *ctxt)
- +{
- + unsigned long flags;
- +
- + spin_lock_irqsave(&ctxt->dev_lock, flags);
- +
- + //fs_reset_config(ctxt);
- +
- + /* free closed ports and disconnect open ports */
- + /* (open ports will be freed when closed) */
- + fs_free_ports(ctxt);
- +
- + /* re-allocate ports for the next connection */
- + if (fs_alloc_ports(ctxt, GFP_ATOMIC) != 0)
- + printk(KERN_ERR "fs_disconnect: cannot re-allocate ports\n");
- +
- + spin_unlock_irqrestore(&ctxt->dev_lock, flags);
- +
- + //printk(KERN_INFO "fs_disconnect: %s disconnected\n", FS_LONG_NAME);
- +}
- +
- +
- +static void fs_configure(int configured, void *context)
- +{
- + struct userial_context * ctxt = context;
- + struct usb_endpoint *ep;
- + struct usb_request *req, *temp;
- + int i = 0;
- + fs_debug_level(3,"fs_configure:configure %d\n", configured);
- + if (configured){
- + ep = ctxt->dev_out_ep;
- + list_for_each_entry_safe(req, temp, &ctxt->dev_read_list, list){
- + list_del(&req->list);
- + req->length = MAX_PKT;
- + i++;
- + if (usb_ept_queue_xfer(ep, req)<0) {
- + printk(KERN_ERR "fs_configure: cannot queue read request\n");
- + }
- + }
- + printk(KERN_ERR "fs_configure queue %d request in read list\n", i);
- + }
- + else{
- + fs_disconnect(ctxt);
- + }
- +
- +}
- +
- +/*
- + * fs_reset_config
- + *
- + * Mark the device as not configured, disable all endpoints,
- + * which forces completion of pending I/O and frees queued
- + * requests, and free the remaining write requests on the
- + * free list.
- + *
- + * The device lock must be held when calling this function.
- + */
- +#if 0
- +static void fs_reset_config(struct userial_context* dev)
- +{
- + struct usb_request *req;
- + printk(KERN_ERR "in the fs_reset_config\n");
- + if (dev == NULL) {
- + printk(KERN_ERR "fs_reset_config: NULL device pointer\n");
- + return;
- + }
- +
- + /* free write requests on the free list */
- + while(!list_empty(&dev->dev_write_list)) {
- + req = list_entry(dev->dev_write_list.next, struct usb_request, list);
- + list_del(&req->list);
- + usb_ept_free_req(dev->dev_in_ep, req);
- + }
- +
- + /* disable endpoints, forcing completion of pending i/o; */
- + /* completion handlers free their requests in this case */
- + //if (dev->dev_in_ep) {
- + // usb_ep_disable(dev->dev_in_ep);
- + // dev->dev_in_ep = NULL;
- + //}
- + //if (dev->dev_out_ep) {
- + // usb_ep_disable(dev->dev_out_ep);
- + // dev->dev_out_ep = NULL;
- + //}
- +}
- +#endif
- +
- +/*
- + * fs_alloc_ports
- + *
- + * Allocate all ports and set the fs_dev struct to point to them.
- + * Return 0 if successful, or a negative error number.
- + *
- + * The device lock is normally held when calling this function.
- + */
- +static int fs_alloc_ports(struct userial_context *ctxt, gfp_t kmalloc_flags)
- +{
- + int i;
- + struct fs_port *port;
- +
- + if (ctxt == NULL)
- + return -EIO;
- +
- + for (i=0; i<FS_NUM_PORTS; i++) {
- + if ((port=kzalloc(sizeof(struct fs_port), kmalloc_flags)) == NULL)
- + return -ENOMEM;
- +
- + port->port_dev = ctxt;
- + port->port_num = i;
- + port->port_line_coding.dwDTERate = cpu_to_le32(FS_DEFAULT_DTE_RATE);
- + port->port_line_coding.bCharFormat = FS_DEFAULT_CHAR_FORMAT;
- + port->port_line_coding.bParityType = FS_DEFAULT_PARITY;
- + port->port_line_coding.bDataBits = FS_DEFAULT_DATA_BITS;
- + spin_lock_init(&port->port_lock);
- + init_waitqueue_head(&port->port_write_wait);
- +
- + ctxt->dev_port[i] = port;
- + }
- +
- + return 0;
- +}
- +
- +/*
- + * fs_free_ports
- + *
- + * Free all closed ports. Open ports are disconnected by
- + * freeing their write buffers, setting their device pointers
- + * and the pointers to them in the device to NULL. These
- + * ports will be freed when closed.
- + *
- + * The device lock is normally held when calling this function.
- +*/
- +
- + static void fs_free_ports(struct userial_context * ctxt)
- +{
- + int i;
- + unsigned long flags;
- + struct fs_port *port;
- +
- + if (ctxt == NULL)
- + return;
- +
- + for (i=0; i<FS_NUM_PORTS; i++) {
- + if ((port=ctxt->dev_port[i]) != NULL) {
- + ctxt->dev_port[i] = NULL;
- +
- + spin_lock_irqsave(&port->port_lock, flags);
- +
- + if (port->port_write_buf != NULL) {
- + fs_buf_free(port->port_write_buf);
- + port->port_write_buf = NULL;
- + }
- +
- + if (port->port_open_count > 0 || port->port_in_use) {
- + port->port_dev = NULL;
- + wake_up_interruptible(&port->port_write_wait);
- + if (port->port_tty) {
- + wake_up_interruptible(&port->port_tty->read_wait);
- + wake_up_interruptible(&port->port_tty->write_wait);
- + }
- + spin_unlock_irqrestore(&port->port_lock, flags);
- + } else {
- + spin_unlock_irqrestore(&port->port_lock, flags);
- + kfree(port);
- + }
- +
- + }
- + }
- +}
- +
- +/* Circular Buffer */
- +
- +/*
- + * fs_buf_alloc
- + *
- + * Allocate a circular buffer and all associated memory.
- + */
- +static struct fs_buf *fs_buf_alloc(unsigned int size, gfp_t kmalloc_flags)
- +{
- + struct fs_buf *gb;
- +
- + if (size == 0)
- + return NULL;
- +
- + gb = kmalloc(sizeof(struct fs_buf), kmalloc_flags);
- + if (gb == NULL)
- + return NULL;
- +
- + gb->buf_buf = kmalloc(size, kmalloc_flags);
- + if (gb->buf_buf == NULL) {
- + kfree(gb);
- + return NULL;
- + }
- +
- + gb->buf_size = size;
- + gb->buf_get = gb->buf_put = gb->buf_buf;
- +
- + return gb;
- +}
- +
- +/*
- + * fs_buf_free
- + *
- + * Free the buffer and all associated memory.
- + */
- +static void fs_buf_free(struct fs_buf *gb)
- +{
- + if (gb) {
- + kfree(gb->buf_buf);
- + kfree(gb);
- + }
- +}
- +
- +/*
- + * fs_buf_clear
- + *
- + * Clear out all data in the circular buffer.
- + */
- +static void fs_buf_clear(struct fs_buf *gb)
- +{
- + if (gb != NULL)
- + gb->buf_get = gb->buf_put;
- + /* equivalent to a get of all data available */
- +}
- +
- +/*
- + * fs_buf_data_avail
- + *
- + * Return the number of bytes of data available in the circular
- + * buffer.
- + */
- +static unsigned int fs_buf_data_avail(struct fs_buf *gb)
- +{
- + if (gb != NULL)
- + return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size;
- + else
- + return 0;
- +}
- +
- +/*
- + * fs_buf_space_avail
- + *
- + * Return the number of bytes of space available in the circular
- + * buffer.
- + */
- +static unsigned int fs_buf_space_avail(struct fs_buf *gb)
- +{
- + if (gb != NULL)
- + return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
- + else
- + return 0;
- +}
- +
- +/*
- + * fs_buf_put
- + *
- + * Copy data data from a user buffer and put it into the circular buffer.
- + * Restrict to the amount of space available.
- + *
- + * Return the number of bytes copied.
- + */
- +static unsigned int
- +fs_buf_put(struct fs_buf *gb, const char *buf, unsigned int count)
- +{
- + unsigned int len;
- +
- + if (gb == NULL)
- + return 0;
- +
- + len = fs_buf_space_avail(gb);
- + if (count > len)
- + count = len;
- +
- + if (count == 0)
- + return 0;
- +
- + len = gb->buf_buf + gb->buf_size - gb->buf_put;
- + if (count > len) {
- + memcpy(gb->buf_put, buf, len);
- + memcpy(gb->buf_buf, buf+len, count - len);
- + gb->buf_put = gb->buf_buf + count - len;
- + } else {
- + memcpy(gb->buf_put, buf, count);
- + if (count < len)
- + gb->buf_put += count;
- + else /* count == len */
- + gb->buf_put = gb->buf_buf;
- + }
- +
- + return count;
- +}
- +
- +/*
- + * fs_buf_get
- + *
- + * Get data from the circular buffer and copy to the given buffer.
- + * Restrict to the amount of data available.
- + *
- + * Return the number of bytes copied.
- + */
- +static unsigned int
- +fs_buf_get(struct fs_buf *gb, char *buf, unsigned int count)
- +{
- + unsigned int len;
- +
- + if (gb == NULL)
- + return 0;
- +
- + len = fs_buf_data_avail(gb);
- + if (count > len)
- + count = len;
- +
- + if (count == 0)
- + return 0;
- +
- + len = gb->buf_buf + gb->buf_size - gb->buf_get;
- + if (count > len) {
- + memcpy(buf, gb->buf_get, len);
- + memcpy(buf+len, gb->buf_buf, count - len);
- + gb->buf_get = gb->buf_buf + count - len;
- + } else {
- + memcpy(buf, gb->buf_get, count);
- + if (count < len)
- + gb->buf_get += count;
- + else /* count == len */
- + gb->buf_get = gb->buf_buf;
- + }
- +
- + return count;
- +}
- diff -Naurp a/drivers/usb/function/fsync.c b/drivers/usb/function/fsync.c
- --- a/drivers/usb/function/fsync.c 1970-01-01 01:00:00.000000000 +0100
- +++ b/drivers/usb/function/fsync.c 2009-12-18 23:15:51.919779912 +0100
- @@ -0,0 +1,677 @@
- +/* drivers/usb/function/fsync.c
- + *
- + * Function Device for the Android ADB Protocol
- + *
- + * Copyright (C) 2007 Google, Inc.
- + * Author: Brian Swetland <swetland@google.com>
- + *
- + * This software is licensed under the terms of the GNU General Public
- + * License version 2, as published by the Free Software Foundation, and
- + * may be copied, distributed, and modified under those terms.
- + *
- + * This program is distributed in the hope that it will be useful,
- + * but WITHOUT ANY WARRANTY; without even the implied warranty of
- + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + * GNU General Public License for more details.
- + *
- + * base on adb, modify to fsync
- + * Author: Anthony_Chang@htc.com
- + */
- +
- +#include <linux/init.h>
- +#include <linux/module.h>
- +#include <linux/kernel.h>
- +#include <linux/miscdevice.h>
- +#include <linux/fs.h>
- +#include <linux/platform_device.h>
- +#include <linux/sched.h>
- +
- +#include <linux/wait.h>
- +#include <linux/list.h>
- +
- +#include <asm/atomic.h>
- +#include <asm/uaccess.h>
- +
- +#include "usb_function.h"
- +
- +#if 1
- +#define DBG(x...) do {} while (0)
- +#else
- +#define DBG(x...) printk(x)
- +#endif
- +
- +/* allocate buffer size to: 16384 byte */
- +//#define ALLOCATE_16K_BUFF
- +
- +#ifdef ALLOCATE_16K_BUFF
- +#define TXN_MAX 16384
- +#else
- +#define TXN_MAX 4096
- +
- +/* number of rx and tx requests to allocate */
- +#define RX_REQ_MAX 4
- +#define TX_REQ_MAX 4
- +#endif
- +
- +#define FSYNC_FUNCTION_NAME "fsync"
- +
- +/* fsync_status in /sys
- + * #define USB_FSYNC_ADD_ATTR_FILE
- + */
- +#ifdef USB_FSYNC_ADD_ATTR_FILE
- +static char fsync_status_tmp[32] = { 0 };
- +#endif
- +struct device fsync_dev;
- +static void fsync_bind(struct usb_endpoint **ept, void *_ctxt);
- +static void fsync_unbind(void *_ctxt);
- +static void fsync_configure(int configured, void *_ctxt);
- +
- +struct fsync_context
- +{
- + int online;
- + int error;
- +
- + atomic_t read_excl;
- + atomic_t write_excl;
- + atomic_t open_excl;
- + atomic_t enable_excl;
- + spinlock_t lock;
- +
- + struct usb_endpoint *out;
- + struct usb_endpoint *in;
- +
- + struct list_head tx_idle;
- + struct list_head rx_idle;
- + struct list_head rx_done;
- +
- + wait_queue_head_t read_wq;
- + wait_queue_head_t write_wq;
- +
- + /* the request we're currently reading from */
- + struct usb_request *read_req;
- + unsigned char *read_buf;
- + unsigned read_count;
- + struct platform_device *pdev;
- + int registered;
- +};
- +
- +static struct fsync_context _context;
- +
- +static struct usb_function usb_func_fsync = {
- + .bind = fsync_bind,
- + .unbind = fsync_unbind,
- + .configure = fsync_configure,
- +
- + .name = FSYNC_FUNCTION_NAME,
- + .context = &_context,
- +
- + .ifc_class = 0xff,
- + .ifc_subclass = 0x43,
- + .ifc_protocol = 0x01,
- +
- + .ifc_name = "fsync",
- +
- + .ifc_ept_count = 2,
- + .position_bit = USB_FUNCTION_FSYNC_NUM,
- + .ifc_ept_type = { EPT_BULK_OUT, EPT_BULK_IN },
- +
- + /* the fsync function is only enabled when its driver file is open */
- + .disabled = 1,
- + .ifc_num = 1,
- +};
- +
- +static inline int _lock(atomic_t *excl)
- +{
- + if (atomic_inc_return(excl) == 1) {
- + return 0;
- + } else {
- + atomic_dec(excl);
- + return -1;
- + }
- +}
- +
- +static inline void _unlock(atomic_t *excl)
- +{
- + atomic_dec(excl);
- +}
- +
- +/* add a request to the tail of a list */
- +static void req_put(struct fsync_context *ctxt, struct list_head *head, struct usb_request *req)
- +{
- + unsigned long flags;
- +
- + spin_lock_irqsave(&ctxt->lock, flags);
- + list_add_tail(&req->list, head);
- + spin_unlock_irqrestore(&ctxt->lock, flags);
- +}
- +
- +/* remove a request from the head of a list */
- +static struct usb_request *req_get(struct fsync_context *ctxt, struct list_head *head)
- +{
- + unsigned long flags;
- + struct usb_request *req;
- +
- + spin_lock_irqsave(&ctxt->lock, flags);
- + if (list_empty(head)) {
- + req = 0;
- + } else {
- + req = list_first_entry(head, struct usb_request, list);
- + list_del(&req->list);
- + }
- + spin_unlock_irqrestore(&ctxt->lock, flags);
- + return req;
- +}
- +
- +static void fsync_complete_in(struct usb_endpoint *ept, struct usb_request *req)
- +{
- + struct fsync_context *ctxt = req->context;
- +
- + if (req->status != 0)
- + ctxt->error = 1;
- +
- + req_put(ctxt, &ctxt->tx_idle, req);
- +
- + wake_up(&ctxt->write_wq);
- +}
- +
- +static void fsync_complete_out(struct usb_endpoint *ept, struct usb_request *req)
- +{
- + struct fsync_context *ctxt = req->context;
- +
- + if (req->status != 0) {
- + ctxt->error = 1;
- + req_put(ctxt, &ctxt->rx_idle, req);
- + } else {
- + req_put(ctxt, &ctxt->rx_done, req);
- + }
- +
- + wake_up(&ctxt->read_wq);
- +}
- +
- +static ssize_t fsync_read(struct file *fp, char __user *buf,
- + size_t count, loff_t *pos)
- +{
- + struct fsync_context *ctxt = &_context;
- + struct usb_request *req;
- + int r = count, xfer;
- + int ret;
- +
- + DBG("%s(%d)\n", __func__, count);
- +
- + if (_lock(&ctxt->read_excl))
- + return -EBUSY;
- +
- + /* we will block until we're online */
- + while (!(ctxt->online || ctxt->error)) {
- + DBG("%s: waiting for online state\n", __func__);
- + ret = wait_event_interruptible(ctxt->read_wq, (ctxt->online || ctxt->error));
- + if (ret < 0) {
- + _unlock(&ctxt->read_excl);
- + return ret;
- + }
- + }
- +
- + while (count > 0) {
- + if (ctxt->error) {
- + r = -EIO;
- + break;
- + }
- +
- + /* if we have idle read requests, get them queued */
- + while ((req = req_get(ctxt, &ctxt->rx_idle))) {
- +requeue_req:
- + req->length = TXN_MAX;
- + ret = usb_ept_queue_xfer(ctxt->out, req);
- + if (ret < 0) {
- + DBG("%s: failed to queue req %p (%d)\n", __func__, req, ret);
- + r = -EIO;
- + ctxt->error = 1;
- + req_put(ctxt, &ctxt->rx_idle, req);
- + goto fail;
- + } else {
- + DBG("%s: rx %p queue\n", __func__, req);
- + }
- + }
- +
- + /* if we have data pending, give it to userspace */
- + if (ctxt->read_count > 0) {
- + xfer = (ctxt->read_count < count) ? ctxt->read_count : count;
- +
- + if (copy_to_user(buf, ctxt->read_buf, xfer)) {
- + r = -EFAULT;
- + break;
- + }
- + ctxt->read_buf += xfer;
- + ctxt->read_count -= xfer;
- + buf += xfer;
- + count -= xfer;
- +
- + /* if we've emptied the buffer, release the request */
- + if (ctxt->read_count == 0) {
- + req_put(ctxt, &ctxt->rx_idle, ctxt->read_req);
- + ctxt->read_req = 0;
- + }
- + continue;
- + }
- +
- + /* wait for a request to complete */
- + req = 0;
- + ret = wait_event_interruptible(ctxt->read_wq,
- + ((req = req_get(ctxt, &ctxt->rx_done)) || ctxt->error));
- +
- + if (req != 0) {
- + /* if we got a 0-len one we need to put it back into
- + ** service. if we made it the current read req we'd
- + ** be stuck forever
- + */
- + if (req->actual == 0)
- + goto requeue_req;
- +
- + ctxt->read_req = req;
- + ctxt->read_count = req->actual;
- + ctxt->read_buf = req->buf;
- + DBG("rx %p %d\n", req, req->actual);
- + }
- +
- + if (ret < 0) {
- + r = ret;
- + break;
- + }
- + }
- +
- +fail:
- + _unlock(&ctxt->read_excl);
- + return r;
- +}
- +
- +static ssize_t fsync_write(struct file *fp, const char __user *buf,
- + size_t count, loff_t *pos)
- +{
- + struct fsync_context *ctxt = &_context;
- + struct usb_request *req = 0;
- + int r = count, xfer;
- + int ret;
- +
- + DBG("%s(%d)\n", __func__, count);
- +
- + if (_lock(&ctxt->write_excl))
- + return -EBUSY;
- +
- + while (count > 0) {
- + if (ctxt->error) {
- + r = -EIO;
- + break;
- + }
- +
- + /* get an idle tx request to use */
- + req = 0;
- + ret = wait_event_interruptible(ctxt->write_wq,
- + ((req = req_get(ctxt, &ctxt->tx_idle)) || ctxt->error));
- +
- + if (ret < 0) {
- + r = ret;
- + break;
- + }
- +
- + if (req != 0) {
- + xfer = count > TXN_MAX ? TXN_MAX : count;
- + if (copy_from_user(req->buf, buf, xfer)) {
- + r = -EFAULT;
- + break;
- + }
- +
- + req->length = xfer;
- + ret = usb_ept_queue_xfer(ctxt->in, req);
- + if (ret < 0) {
- + DBG("%s: xfer error %d\n", __func__, ret);
- + ctxt->error = 1;
- + r = -EIO;
- + break;
- + }
- +
- + buf += xfer;
- + count -= xfer;
- +
- + /* zero this so we don't try to free it on error exit */
- + req = 0;
- + }
- + }
- +
- +
- + if (req)
- + req_put(ctxt, &ctxt->tx_idle, req);
- +
- + _unlock(&ctxt->write_excl);
- + return r;
- +}
- +
- +static int fsync_open(struct inode *ip, struct file *fp)
- +{
- + struct fsync_context *ctxt = &_context;
- +
- + if (_lock(&ctxt->open_excl))
- + return -EBUSY;
- +
- + /* clear the error latch */
- + ctxt->error = 0;
- +
- + return 0;
- +}
- +
- +static int fsync_release(struct inode *ip, struct file *fp)
- +{
- + struct fsync_context *ctxt = &_context;
- +
- + _unlock(&ctxt->open_excl);
- + return 0;
- +}
- +
- +static struct file_operations fsync_fops = {
- + .owner = THIS_MODULE,
- + .read = fsync_read,
- + .write = fsync_write,
- + .open = fsync_open,
- + .release = fsync_release,
- +};
- +
- +static struct miscdevice fsync_device = {
- + .minor = MISC_DYNAMIC_MINOR,
- + .name = "android_fsync",
- + .fops = &fsync_fops,
- +};
- +
- +static int fsync_enable_open(struct inode *ip, struct file *fp)
- +{
- + struct fsync_context *ctxt = &_context;
- +
- + if (_lock(&ctxt->enable_excl))
- + return -EBUSY;
- +
- + printk(KERN_INFO "%s: enabling fsync function\n", __func__);
- + usb_function_enable(FSYNC_FUNCTION_NAME, 1);
- + /* clear the error latch */
- + ctxt->error = 0;
- +
- + return 0;
- +}
- +
- +static int fsync_enable_release(struct inode *ip, struct file *fp)
- +{
- + struct fsync_context *ctxt = &_context;
- +
- + printk(KERN_INFO "%s: disabling fsync function\n", __func__);
- + usb_function_enable(FSYNC_FUNCTION_NAME, 0);
- + _unlock(&ctxt->enable_excl);
- + return 0;
- +}
- +
- +static struct file_operations fsync_enable_fops = {
- + .owner = THIS_MODULE,
- + .open = fsync_enable_open,
- + .release = fsync_enable_release,
- +};
- +
- +static struct miscdevice fsync_enable_device = {
- + .minor = MISC_DYNAMIC_MINOR,
- + .name = "android_fsync_en",
- + .fops = &fsync_enable_fops,
- +};
- +
- +#ifdef USB_FSYNC_ADD_ATTR_FILE
- +static ssize_t show_fsync_status(struct device *dev, struct device_attribute *attr,
- + char *buf)
- +{
- + unsigned length;
- + //struct msm_hsusb_platform_data *pdata = dev->platform_data;
- +
- + if(strlen(fsync_status_tmp))
- + length = sprintf(buf, "%s", fsync_status_tmp); /* dummy */
- + else
- + length = 0;
- + return length;
- +}
- +
- +static ssize_t store_fsync_status(struct device *dev, struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- + //struct usb_info *ui = the_usb_info;
- + int data_buff_size = (sizeof(fsync_status_tmp)>strlen(buf))?
- + strlen(buf):sizeof(fsync_status_tmp);
- + int loop_i;
- +
- + /* avoid overflow, fsync_status_tmp[32] always is 0x0 */
- + if(data_buff_size == 32)
- + data_buff_size--;
- +
- + for(loop_i = 0; loop_i < data_buff_size; loop_i++) {
- + if(buf[loop_i] >= 0x30 && buf[loop_i] <= 0x39) /* 0-9 */
- + continue;
- + else
- + if(buf[loop_i] >= 0x41 && buf[loop_i] <= 0x5A) /* A-Z */
- + continue;
- + else
- + if(buf[loop_i] >= 0x61 && buf[loop_i] <= 0x7A) /* a-z */
- + continue;
- + else
- + if(buf[loop_i] == 0x0A) /* Line Feed */
- + continue;
- + else
- + {
- + printk(KERN_WARNING "%s(): get invaild char (0x%2.2X)\n", __func__, buf[loop_i]);
- + return -EINVAL;
- + }
- + }
- +
- + memset(fsync_status_tmp, 0x0, sizeof(fsync_status_tmp));
- + strncpy(fsync_status_tmp, buf, data_buff_size);
- +
- + return count;
- +}
- +
- +static DEVICE_ATTR(fsync_status, 0644,
- + show_fsync_status, store_fsync_status);
- +#endif
- +
- +static int fsync_probe_reg (struct platform_device *pdev)
- +{
- + struct fsync_context *ctxt = &_context;
- + ctxt->pdev = pdev;
- + return 0;
- +}
- +
- +static struct platform_driver fsync_driver_reg = {
- + .probe = fsync_probe_reg,
- + .driver = { .name = FSYNC_FUNCTION_NAME, },
- +};
- +
- +static void fsync_release_reg (struct device *dev) {}
- +
- +static struct platform_device fsync_device_reg = {
- + .name = FSYNC_FUNCTION_NAME,
- + .id = -1,
- + .dev = {
- + .release = fsync_release_reg,
- + },
- +};
- +
- +static void fsync_dev_release (struct device *dev) {}
- +
- +static void fsync_unbind(void *_ctxt)
- +{
- + struct fsync_context *ctxt = _ctxt;
- + struct usb_request *req;
- +
- + printk(KERN_INFO "%s()\n", __func__);
- +
- + while ((req = req_get(ctxt, &ctxt->rx_idle))) {
- + usb_ept_free_req(ctxt->out, req);
- + }
- + while ((req = req_get(ctxt, &ctxt->tx_idle))) {
- + usb_ept_free_req(ctxt->in, req);
- + }
- + if (ctxt->registered) {
- +#ifdef USB_FSYNC_ADD_ATTR_FILE
- + device_remove_file(&fsync_dev, &dev_attr_fsync_status);
- +#endif
- + device_unregister(&fsync_dev);
- + ctxt->registered = 0;
- + }
- +
- + ctxt->online = 0;
- + ctxt->error = 1;
- +
- + /* readers may be blocked waiting for us to go online */
- + wake_up(&ctxt->read_wq);
- +}
- +
- +static void fsync_bind(struct usb_endpoint **ept, void *_ctxt)
- +{
- + struct fsync_context *ctxt = _ctxt;
- + struct usb_request *req;
- + int ret;
- +#ifndef ALLOCATE_16K_BUFF
- + int n;
- +#endif
- +
- + ctxt->out = ept[0];
- + ctxt->in = ept[1];
- + ctxt->registered = 0;
- + printk(KERN_INFO "%s() %p, %p\n", __func__, ctxt->out, ctxt->in);
- +
- +#ifndef ALLOCATE_16K_BUFF
- + for (n = 0; n < RX_REQ_MAX; n++) {
- +#else
- + {
- +#endif
- +
- + req = usb_ept_alloc_req(ctxt->out, TXN_MAX);
- + if (req == 0) goto fail;
- + req->context = ctxt;
- + req->complete = fsync_complete_out;
- + req_put(ctxt, &ctxt->rx_idle, req);
- + }
- +
- +
- +#ifndef ALLOCATE_16K_BUFF
- + for (n = 0; n < TX_REQ_MAX; n++) {
- +#else
- + {
- +#endif
- + req = usb_ept_alloc_req(ctxt->in, TXN_MAX);
- + if (req == 0) goto fail;
- + req->context = ctxt;
- + req->complete = fsync_complete_in;
- + req_put(ctxt, &ctxt->tx_idle, req);
- + }
- +
- +#ifndef ALLOCATE_16K_BUFF
- + printk(KERN_INFO
- + "%s() allocated %d rx and %d tx requests\n",
- + __func__, RX_REQ_MAX, TX_REQ_MAX);
- +#else
- + printk(KERN_INFO
- + "%s(): allocated buffer: %d\n", __func__, TXN_MAX);
- +#endif
- +
- + misc_register(&fsync_device);
- + misc_register(&fsync_enable_device);
- +
- + fsync_dev.release = fsync_dev_release;
- + fsync_dev.parent = &ctxt->pdev->dev;
- + strcpy(fsync_dev.bus_id, "interface");
- +
- + ret = device_register(&fsync_dev);
- + if (ret != 0) {
- + printk(KERN_WARNING "fsync_dev failed to register device: %d\n", ret);
- + goto fail;
- + }
- +#ifdef USB_FSYNC_ADD_ATTR_FILE
- + ret = device_create_file(&fsync_dev, &dev_attr_fsync_status);
- + if (ret != 0) {
- + printk(KERN_WARNING "fsync_dev device_create_file failed: %d\n", ret);
- + device_unregister(&fsync_dev);
- + goto fail;
- + }
- +#endif
- + ctxt->registered = 1;
- +
- + return;
- +
- +fail:
- + printk(KERN_ERR "%s() could not allocate requests\n", __func__);
- + fsync_unbind(ctxt);
- +}
- +
- +static void fsync_configure(int configured, void *_ctxt)
- +{
- + struct fsync_context *ctxt = _ctxt;
- + struct usb_request *req;
- +
- + DBG("%s(): %s\n", __func__, (configured)?"success":"failure");
- +
- + if (configured) {
- + ctxt->online = 1;
- +
- + /* if we have a stale request being read, recycle it */
- + ctxt->read_buf = 0;
- + ctxt->read_count = 0;
- + if (ctxt->read_req) {
- + req_put(ctxt, &ctxt->rx_idle, ctxt->read_req);
- + ctxt->read_req = 0;
- + }
- +
- + /* retire any completed rx requests from previous session */
- + while ((req = req_get(ctxt, &ctxt->rx_done)))
- + req_put(ctxt, &ctxt->rx_idle, req);
- +
- + } else {
- + ctxt->online = 0;
- + ctxt->error = 1;
- + }
- +
- + /* readers may be blocked waiting for us to go online */
- + wake_up(&ctxt->read_wq);
- +}
- +
- +static int __init fsync_init(void)
- +{
- + struct fsync_context *ctxt = &_context;
- + int retval = 0;
- + DBG("%s()\n", __func__);
- +
- + init_waitqueue_head(&ctxt->read_wq);
- + init_waitqueue_head(&ctxt->write_wq);
- +
- + atomic_set(&ctxt->open_excl, 0);
- + atomic_set(&ctxt->read_excl, 0);
- + atomic_set(&ctxt->write_excl, 0);
- + atomic_set(&ctxt->enable_excl, 0);
- +
- + spin_lock_init(&ctxt->lock);
- +
- + INIT_LIST_HEAD(&ctxt->rx_idle);
- + INIT_LIST_HEAD(&ctxt->rx_done);
- + INIT_LIST_HEAD(&ctxt->tx_idle);
- +
- + retval = platform_driver_register (&fsync_driver_reg);
- + if (retval < 0)
- + return retval;
- + retval = platform_device_register (&fsync_device_reg);
- + if (retval < 0)
- + goto err_register_device_1;
- +
- + retval = usb_function_register(&usb_func_fsync);
- + if (retval < 0)
- + goto err_register_device_2;
- +
- + return retval;
- +
- +err_register_device_2:
- + platform_device_unregister(&fsync_device_reg);
- +err_register_device_1:
- + platform_driver_unregister(&fsync_driver_reg);
- + return retval;
- +
- +}
- +
- +module_init(fsync_init);
- diff -Naurp a/drivers/usb/function/Kconfig b/drivers/usb/function/Kconfig
- --- a/drivers/usb/function/Kconfig 2009-12-18 22:01:12.191477285 +0100
- +++ b/drivers/usb/function/Kconfig 2009-12-18 22:01:22.015489838 +0100
- @@ -50,4 +50,32 @@ config USB_FUNCTION_ETHER
- boolean "USB Ethernet Function"
- depends on USB_FUNCTION
- +config USB_FUNCTION_RNDIS
- + boolean "USB RNDIS support"
- + depends on USB_FUNCTION_ETHER
- + default y
- +
- +config USB_FUNCTION_RNDIS_WCEIS
- + boolean "Use Windows Internet Sharing Class/SubClass/Protocol"
- + depends on USB_FUNCTION_RNDIS
- + default y
- + help
- + Causes the driver to look like a Windows-compatible Internet
- + Sharing device, so Windows auto-detects it.
- +
- + If you enable this option, the device is no longer CDC ethernet
- + compatible.
- +
- +config USB_FUNCTION_FSYNC
- + boolean "USB MSM7K FSYNC Function"
- + depends on USB_FUNCTION
- +
- +config USB_FUNCTION_SERIAL
- + boolean "USB SERIAL Function"
- + depends on USB_FUNCTION
- +
- +config USB_FUNCTION_MTP_TUNNEL
- + boolean "MTP Tunnel Transport Function"
- + depends on USB_FUNCTION
- +
- endmenu
- diff -Naurp a/drivers/usb/function/loopback.c b/drivers/usb/function/loopback.c
- --- a/drivers/usb/function/loopback.c 2009-12-18 22:09:33.031478757 +0100
- +++ b/drivers/usb/function/loopback.c 2009-12-18 22:09:40.823480975 +0100
- @@ -117,6 +117,8 @@ static struct usb_function usb_func_loop
- .ifc_ept_count = 2,
- .ifc_ept_type = { EPT_BULK_OUT, EPT_BULK_IN },
- +
- + .position_bit = USB_FUNCTION_LOOPBACK_NUM,
- };
- static int __init loopback_init(void)
- diff -Naurp a/drivers/usb/function/Makefile b/drivers/usb/function/Makefile
- --- a/drivers/usb/function/Makefile 2009-12-18 22:04:21.175484872 +0100
- +++ b/drivers/usb/function/Makefile 2009-12-18 22:04:33.815493233 +0100
- @@ -1,10 +1,14 @@
- obj-$(CONFIG_USB_FUNCTION_MSM_HSUSB) += msm_hsusb.o
- obj-$(CONFIG_USB_FUNCTION_NULL) += null.o
- -obj-$(CONFIG_USB_FUNCTION_NULL) += zero.o
- +obj-$(CONFIG_USB_FUNCTION_ZERO) += zero.o
- obj-$(CONFIG_USB_FUNCTION_LOOPBACK) += loopback.o
- obj-$(CONFIG_USB_FUNCTION_ADB) += adb.o
- obj-$(CONFIG_USB_FUNCTION_UMS) += ums.o
- obj-$(CONFIG_USB_FUNCTION_MASS_STORAGE) += mass_storage.o
- obj-$(CONFIG_USB_FUNCTION_DIAG) += diag.o
- obj-$(CONFIG_USB_FUNCTION_ETHER) += ether.o
- +obj-$(CONFIG_USB_FUNCTION_RNDIS) += rndis.o
- +obj-$(CONFIG_USB_FUNCTION_SERIAL) += fserial.o
- +obj-$(CONFIG_USB_FUNCTION_FSYNC) += fsync.o
- +obj-$(CONFIG_USB_FUNCTION_MTP_TUNNEL) += mtp_tunnel.o
- diff -Naurp a/drivers/usb/function/mass_storage.c b/drivers/usb/function/mass_storage.c
- --- a/drivers/usb/function/mass_storage.c 2009-12-18 22:09:54.103459621 +0100
- +++ b/drivers/usb/function/mass_storage.c 2009-12-18 22:10:01.175483175 +0100
- @@ -2449,9 +2449,39 @@ static ssize_t store_file(struct device
- return (rc < 0 ? rc : count);
- }
- -
- static DEVICE_ATTR(file, 0444, show_file, store_file);
- +static ssize_t store_enable(struct device *dev, struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- + unsigned long ul;
- + if (count > 2 || (buf[0] != '0' && buf[0] != '1')) {
- + printk(KERN_ERR "Can't enable/disable %s %s\n", DRIVER_NAME, buf);
- + return -EINVAL;
- + }
- + ul = simple_strtoul(buf, NULL, 10);
- + usb_function_enable(DRIVER_NAME, ul);
- +
- + return count;
- +}
- +
- +static ssize_t show_enable(struct device *dev, struct device_attribute *attr,
- + char *buf)
- +{
- + if (return_usb_function_enabled(DRIVER_NAME) > 0) {
- + buf[0] = '1';
- + buf[1] = '\n';
- + }
- + else {
- + buf[0] = '0';
- + buf[1] = '\n';
- + }
- +
- + return 2;
- +}
- +
- +static DEVICE_ATTR(enable, 0644, show_enable, store_enable);
- +
- /*-------------------------------------------------------------------------*/
- static void fsg_release(struct kref *ref)
- @@ -2483,6 +2513,7 @@ static void /* __init_or_exit */ fsg_unb
- curlun = &fsg->luns[i];
- if (curlun->registered) {
- device_remove_file(&curlun->dev, &dev_attr_file);
- + device_remove_file(&curlun->dev, &dev_attr_enable);
- device_unregister(&curlun->dev);
- curlun->registered = 0;
- }
- @@ -2554,6 +2585,12 @@ static void fsg_bind(struct usb_endpoint
- device_unregister(&curlun->dev);
- goto out;
- }
- + rc = device_create_file(&curlun->dev, &dev_attr_enable);
- + if (rc != 0) {
- + ERROR(fsg, "device_create_file failed: %d\n", rc);
- + device_unregister(&curlun->dev);
- + goto out;
- + }
- curlun->registered = 1;
- kref_get(&fsg->ref);
- }
- @@ -2622,6 +2659,8 @@ static void fsg_configure(int configured
- /*-------------------------------------------------------------------------*/
- +/*-------------------------------------------------------------------------*/
- +
- static struct usb_function fsg_function = {
- .bind = fsg_bind,
- .unbind = fsg_unbind,
- @@ -2638,6 +2677,8 @@ static struct usb_function fsg_function
- .ifc_ept_count = 2,
- .ifc_ept_type = { EPT_BULK_OUT, EPT_BULK_IN },
- +
- + .position_bit = USB_FUNCTION_MASS_STORAGE_NUM,
- };
- diff -Naurp a/drivers/usb/function/msm_hsusb.c b/drivers/usb/function/msm_hsusb.c
- --- a/drivers/usb/function/msm_hsusb.c 2009-12-18 22:10:20.027483557 +0100
- +++ b/drivers/usb/function/msm_hsusb.c 2009-12-18 22:10:26.539476376 +0100
- @@ -53,18 +53,14 @@
- #define STRING_SERIAL 1
- #define STRING_PRODUCT 2
- #define STRING_MANUFACTURER 3
- +#define STRING_ENABLED_START 4
- +#define STRING_DISABLED_START (STRING_ENABLED_START + 32)
- #define LANGUAGE_ID 0x0409 /* en-US */
- /* current state of VBUS */
- static int vbus;
- -struct usb_fi_ept
- -{
- - struct usb_endpoint *ept;
- - struct usb_endpoint_descriptor desc;
- -};
- -
- struct usb_function_info
- {
- struct list_head list;
- @@ -110,6 +106,8 @@ struct usb_endpoint
- unsigned char bit;
- unsigned char num;
- + unsigned char type;
- +
- unsigned short max_pkt;
- /* pointers to DMA transfer list area */
- @@ -162,7 +160,13 @@ struct usb_info
- /* used for allocation */
- unsigned next_item;
- - unsigned next_ifc_num;
- +
- + uint32_t enabled_functions;
- + uint32_t included_functions;
- +
- + unsigned ifc_num;
- + unsigned ifc_map_to_host[32];
- + unsigned ifc_map_to_driver[64];
- /* endpoints are ordered based on their status bits,
- ** so they are OUT0, OUT1, ... OUT15, IN0, IN1, ... IN15
- @@ -192,7 +196,7 @@ struct usb_device_descriptor desc_device
- .bLength = USB_DT_DEVICE_SIZE,
- .bDescriptorType = USB_DT_DEVICE,
- - .bcdUSB = 0x0102,
- + .bcdUSB = 0x0200,
- .bDeviceClass = 0,
- .bDeviceSubClass = 0,
- .bDeviceProtocol = 0,
- @@ -207,8 +211,6 @@ struct usb_device_descriptor desc_device
- .bNumConfigurations = 1,
- };
- -static uint32_t enabled_functions = 0;
- -
- static void flush_endpoint(struct usb_endpoint *ept);
- #if 0
- @@ -290,7 +292,7 @@ static void configure_endpoints(struct u
- unsigned n;
- unsigned cfg;
- - for (n = 0; n < 31; n++) {
- + for (n = 0; n < 32; n++) {
- struct usb_endpoint *ept = ui->ept + n;
- cfg = CONFIG_MAX_PKT(ept->max_pkt) | CONFIG_ZLT;
- @@ -315,7 +317,7 @@ static struct usb_endpoint *alloc_endpoi
- unsigned kind)
- {
- unsigned n;
- - for (n = 0; n < 31; n++) {
- + for (n = 0; n < 32; n++) {
- struct usb_endpoint *ept = ui->ept + n;
- if (ept->num == 0)
- continue;
- @@ -327,12 +329,20 @@ static struct usb_endpoint *alloc_endpoi
- ept->max_pkt = 512;
- ept->owner = owner;
- return ept;
- + } else if (kind == EPT_INT_IN) {
- + ept->max_pkt = 64;
- + ept->owner = owner;
- + return ept;
- }
- } else {
- if (kind == EPT_BULK_OUT) {
- ept->max_pkt = 512;
- ept->owner = owner;
- return ept;
- + } else if (kind == EPT_INT_OUT) {
- + ept->max_pkt = 64;
- + ept->owner = owner;
- + return ept;
- }
- }
- }
- @@ -343,7 +353,7 @@ static struct usb_endpoint *alloc_endpoi
- static void free_endpoints(struct usb_info *ui, struct usb_function_info *owner)
- {
- unsigned n;
- - for (n = 0; n < 31; n++) {
- + for (n = 0; n < 32; n++) {
- struct usb_endpoint *ept = ui->ept + n;
- if (ept->owner == owner) {
- ept->owner = 0;
- @@ -413,26 +423,38 @@ static void usb_ept_enable(struct usb_en
- {
- struct usb_info *ui = ept->ui;
- int in = ept->flags & EPT_FLAG_IN;
- + unsigned char type = ept->type;
- unsigned n;
- if (yes) {
- - if (ui->speed == USB_SPEED_HIGH)
- - ept->max_pkt = 512;
- - else
- + if (ui->speed == USB_SPEED_HIGH) {
- + if (type == EPT_BULK_IN || type == EPT_BULK_OUT) {
- + ept->max_pkt = 512;
- + } else {
- + ept->max_pkt = 64;
- + }
- + } else {
- ept->max_pkt = 64;
- + }
- }
- n = readl(USB_ENDPTCTRL(ept->num));
- if (in) {
- - n = (n & (~CTRL_TXT_MASK)) | CTRL_TXT_BULK;
- + if (type == EPT_BULK_IN)
- + n = (n & (~CTRL_TXT_MASK)) | CTRL_TXT_BULK;
- + else if (type == EPT_INT_IN)
- + n = (n & (~CTRL_TXT_MASK)) | CTRL_TXT_INT;
- if (yes) {
- n |= CTRL_TXE | CTRL_TXR;
- } else {
- n &= (~CTRL_TXE);
- }
- } else {
- - n = (n & (~CTRL_RXT_MASK)) | CTRL_RXT_BULK;
- + if (type == EPT_BULK_OUT)
- + n = (n & (~CTRL_RXT_MASK)) | CTRL_RXT_BULK;
- + else if (type == EPT_INT_OUT)
- + n = (n & (~CTRL_RXT_MASK)) | CTRL_RXT_INT;
- if (yes) {
- n |= CTRL_RXE | CTRL_RXR;
- } else {
- @@ -715,37 +737,39 @@ static void handle_setup(struct usb_info
- if ((ctl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) {
- /* let functions handle vendor and class requests */
- - int i;
- - for (i = 0; i < ui->num_funcs; i++) {
- + if (/*ctl.wIndex >= 0 &&*/ ctl.wIndex < ui->ifc_num) {
- + int i = ui->ifc_map_to_driver[ctl.wIndex];
- struct usb_function_info *fi = ui->func[i];
- - if (fi->func->setup) {
- - if (ctl.bRequestType & USB_DIR_IN) {
- - /* IN request */
- - struct usb_request *req = ui->setup_req;
- -
- - int ret = fi->func->setup(&ctl,
- - req->buf,
- - SETUP_BUF_SIZE,
- - fi->func->context);
- - if (ret >= 0) {
- - req->length = ret;
- - ep0_setup_send(ui, ctl.wLength);
- - return;
- - }
- - } else {
- - /* OUT request */
- - /* FIXME - support reading setup
- - * data from host.
- - */
- - int ret = fi->func->setup(&ctl, NULL, 0,
- - fi->func->context);
- - if (ret >= 0) goto ack;
- + if (!fi->enabled)
- + goto bad_setup_request;
- +
- + if (ctl.bRequestType & USB_DIR_IN) {
- + /* IN request */
- + struct usb_request *req = ui->setup_req;
- +
- + int ret = fi->func->setup(&ctl,
- + req->buf,
- + SETUP_BUF_SIZE,
- + fi->func->context);
- + if (ret >= 0) {
- + req->length = ret;
- + ep0_setup_send(ui, ctl.wLength);
- + return;
- }
- + } else {
- + /* OUT request */
- + /* FIXME - support reading setup
- + * data from host.
- + */
- + int ret = fi->func->setup(&ctl, NULL, 0,
- + fi->func->context);
- + if (ret >= 0) goto ack;
- }
- }
- }
- +bad_setup_request:
- ep0_setup_stall(ui);
- return;
- @@ -968,7 +992,9 @@ static void usb_prepare(struct usb_info
- /* only important for reset/reinit */
- memset(ui->ept, 0, sizeof(ui->ept));
- ui->next_item = 0;
- - ui->next_ifc_num = 0;
- + ui->ifc_num = 0;
- + ui->enabled_functions = 0;
- + ui->included_functions = 0;
- init_endpoints(ui);
- @@ -996,7 +1022,7 @@ static void usb_bind_driver(struct usb_i
- {
- struct usb_endpoint *ept;
- struct usb_endpoint_descriptor *ed;
- - struct usb_endpoint *elist[8];
- + struct usb_endpoint *elist[10];
- struct usb_function *func = fi->func;
- unsigned n, count;
- @@ -1009,12 +1035,14 @@ static void usb_bind_driver(struct usb_i
- fi->ifc.bLength = USB_DT_INTERFACE_SIZE;
- fi->ifc.bDescriptorType = USB_DT_INTERFACE;
- + /* Set while building the descriptor - see usb_find_descriptor */
- + fi->ifc.bInterfaceNumber = 0;
- fi->ifc.bAlternateSetting = 0;
- - fi->ifc.bNumEndpoints = count;
- + fi->ifc.bNumEndpoints = count != 2 ? 1 : count;
- fi->ifc.bInterfaceClass = func->ifc_class;
- fi->ifc.bInterfaceSubClass = func->ifc_subclass;
- fi->ifc.bInterfaceProtocol = func->ifc_protocol;
- - fi->ifc.iInterface = 0;
- + fi->ifc.iInterface = func->ifc_name ? (STRING_ENABLED_START + func->position_bit) : 0;
- for (n = 0; n < count; n++) {
- ept = alloc_endpoint(ui, fi, func->ifc_ept_type[n]);
- @@ -1029,14 +1057,19 @@ static void usb_bind_driver(struct usb_i
- ed->bLength = USB_DT_ENDPOINT_SIZE;
- ed->bDescriptorType = USB_DT_ENDPOINT;
- ed->bEndpointAddress = ept->num | ((ept->flags & EPT_FLAG_IN) ? 0x80 : 0);
- - ed->bmAttributes = 0x02; /* XXX hardcoded bulk */
- + ed->bmAttributes =
- + (func->ifc_ept_type[n] ==
- + EPT_INT_IN || func->ifc_ept_type[n] ==
- + EPT_INT_OUT) ? 0x03 : 0x02;
- ed->bInterval = 0; /* XXX hardcoded bulk */
- elist[n] = ept;
- + ept->type = func->ifc_ept_type[n];
- fi->ept[n].ept = ept;
- }
- - fi->ifc.bInterfaceNumber = ui->next_ifc_num++;
- + elist[count] = &ui->ep0in;
- + elist[count + 1] = &ui->ep0out;
- fi->endpoints = count;
- @@ -1058,6 +1091,12 @@ static void usb_reset(struct usb_info *u
- msleep(2);
- #endif
- + /* disable usb interrupts */
- + writel(0, USB_USBINTR);
- +
- + /* wait for a while after enable usb clk*/
- + msleep(5);
- +
- /* RESET */
- writel(2, USB_USBCMD);
- msleep(10);
- @@ -1066,7 +1105,8 @@ static void usb_reset(struct usb_info *u
- ui->phy_reset();
- /* INCR4 BURST mode */
- - writel(0x01, USB_SBUSCFG);
- + /*boost performance to fix CRC error.*/
- + writel(0x02, USB_SBUSCFG);
- /* select DEVICE mode */
- writel(0x12, USB_USBMODE);
- @@ -1145,6 +1185,14 @@ static struct usb_function_info *usb_fin
- return NULL;
- }
- +struct usb_fi_ept *get_ept_info(const char *function)
- +{
- + struct usb_function_info *fi = usb_find_function(function);
- + if (!fi)
- + return NULL;
- + return &fi->ept[0];
- +}
- +
- static void usb_try_to_bind(void)
- {
- struct usb_info *ui = the_usb_info;
- @@ -1197,7 +1245,15 @@ int usb_function_register(struct usb_fun
- fail:
- mutex_unlock(&usb_function_list_lock);
- - return 0;
- + return ret;
- +}
- +
- +int return_usb_function_enabled(const char *function)
- +{
- + struct usb_function_info *fi = usb_find_function(function);
- + if (!fi)
- + return -EINVAL;
- + return fi->enabled;
- }
- void usb_function_enable(const char *function, int enable)
- @@ -1585,7 +1641,7 @@ static int __init usb_init(void)
- module_init(usb_init);
- -static void copy_string_descriptor(char *string, char *buffer)
- +static void copy_string_descriptor(const char *string, char *buffer)
- {
- int length, i;
- @@ -1600,10 +1656,84 @@ static void copy_string_descriptor(char
- }
- }
- +static const char string_descriptor_disabled[] = " \0(\0d\0i\0s\0a\0b\0l\0e\0d\0)\0\0";
- +static void string_descriptor_append_disabled(char *buffer)
- +{
- + if (buffer[0] < 2) {
- + buffer[0] = 2;
- + buffer[1] = USB_DT_STRING;
- + buffer[2] = 0;
- + buffer[3] = 0;
- + }
- +
- + memcpy(buffer + buffer[0], string_descriptor_disabled, sizeof(string_descriptor_disabled));
- + buffer[0] += sizeof(string_descriptor_disabled) - 2;
- +}
- +
- +static char *copy_ifc_descriptors(char *buf, size_t len, struct usb_info *ui, int i)
- +{
- + struct usb_function_info *fi = ui->func[i];
- + int j, n;
- + char *ptr = buf;
- +
- + /* check to see if the function was enabled when we
- + ** received the USB_DT_DEVICE request to make ensure
- + ** that the interfaces we return here match the
- + ** product ID we returned in the USB_DT_DEVICE.
- + */
- + if (ui->included_functions & BIT(fi->func->position_bit)) {
- + if (!(ui->enabled_functions & BIT(fi->func->position_bit))) {
- + for (j = 0; j < fi->func->ifc_num || j < 1; j++) {
- + /* Copy a disabled interface */
- + struct usb_interface_descriptor *ifc = (struct usb_interface_descriptor *)ptr;
- +
- + if (unlikely(len < (ptr - buf + USB_DT_INTERFACE_SIZE)))
- + return buf;
- +
- + ifc->bLength = USB_DT_INTERFACE_SIZE;
- + ifc->bDescriptorType = USB_DT_INTERFACE;
- + ifc->bInterfaceNumber = ui->ifc_map_to_host[i];
- + ifc->bAlternateSetting = 0;
- + ifc->bNumEndpoints = 0;
- + ifc->bInterfaceClass = 0;
- + ifc->bInterfaceSubClass = 0;
- + ifc->bInterfaceProtocol = 0;
- + ifc->iInterface = fi->func->ifc_name ? (STRING_DISABLED_START + fi->func->position_bit) : 0;
- +
- + ptr += ifc->bLength;
- + }
- + } else {
- + /* Copy the real interface */
- + if (fi->func->ifc_copy) {
- + ptr += fi->func->ifc_copy(ptr, len - (ptr - buf), ui->ifc_map_to_host[i]);
- + } else {
- + if (unlikely(len < (ptr - buf + fi->ifc.bLength)))
- + return buf;
- +
- + memcpy(ptr, &fi->ifc, fi->ifc.bLength);
- +
- + /* Fixup interface number */
- + ptr[2/*bInterfaceNumber*/] = ui->ifc_map_to_host[i];
- +
- + ptr += fi->ifc.bLength;
- +
- + for (n = 0; n < fi->endpoints; n++) {
- + /* XXX hardcoded bulk */
- + fi->ept[n].desc.wMaxPacketSize = fi->ept[n].ept->max_pkt;
- + memcpy(ptr, &(fi->ept[n].desc), fi->ept[n].desc.bLength);
- + ptr += fi->ept[n].desc.bLength;
- + }
- + }
- + }
- + }
- +
- + return ptr;
- +}
- +
- static int usb_find_descriptor(struct usb_info *ui, unsigned id, struct usb_request *req)
- {
- struct msm_hsusb_platform_data *pdata = ui->pdev->dev.platform_data;
- - int i;
- + int i, j;
- unsigned type = id >> 8;
- id &= 0xff;
- @@ -1616,24 +1746,57 @@ static int usb_find_descriptor(struct us
- ** USB_DT_DEVICE matches the list of interfaces we
- ** return for USB_DT_CONFIG.
- */
- - enabled_functions = 0;
- + ui->enabled_functions = 0;
- + ui->included_functions = (uint32_t)-1;
- for (i = 0; i < ui->num_funcs; i++) {
- struct usb_function_info *fi = ui->func[i];
- if (fi->enabled)
- - enabled_functions |= (1 << i);
- + ui->enabled_functions |= (1 << fi->func->position_bit);
- + }
- +
- + if (ui->enabled_functions & BIT(USB_FUNCTION_INTERNET_SHARING_NUM)) {
- +#ifdef CONFIG_USB_FUNCTION_RNDIS_WCEIS
- + desc_device.bDeviceClass = USB_CLASS_WIRELESS_CONTROLLER;
- + desc_device.bDeviceSubClass = 1;
- + desc_device.bDeviceProtocol = 3;
- +#else
- + desc_device.bDeviceClass = USB_CLASS_COMM;
- + desc_device.bDeviceSubClass = 0;
- + desc_device.bDeviceProtocol = 0;
- +#endif
- + } else {
- + desc_device.bDeviceClass = 0;
- + desc_device.bDeviceSubClass = 0;
- + desc_device.bDeviceProtocol = 0;
- }
- /* default product ID */
- desc_device.idProduct = pdata->product_id;
- +
- /* set idProduct based on which functions are enabled */
- for (i = 0; i < pdata->num_products; i++) {
- -
- - if (pdata->products[i].functions == enabled_functions) {
- + if ((pdata->products[i].functions & ui->enabled_functions) == ui->enabled_functions) {
- struct msm_hsusb_product *product =
- &pdata->products[i];
- - if (product->functions == enabled_functions)
- - desc_device.idProduct =
- - product->product_id;
- + desc_device.idProduct =
- + product->product_id;
- + ui->included_functions = pdata->products[i].functions;
- + break;
- + }
- + }
- +
- + if (ui->included_functions != ui->enabled_functions)
- + printk(KERN_WARNING "msm_hsusb: No exact match for %x, using product id %04x with %x\n", ui->enabled_functions, desc_device.idProduct, ui->included_functions);
- +
- + /* Build a host->driver interface number map
- + and driver function number->host interface number map */
- + ui->ifc_num = 0;
- + for (i = 0; i < ui->num_funcs; i++) {
- + struct usb_function_info *fi = ui->func[i];
- + if (ui->included_functions & BIT(fi->func->position_bit)) {
- + ui->ifc_map_to_host[i] = ui->ifc_num;
- + for (j = 0; j < fi->func->ifc_num || j < 1; j++)
- + ui->ifc_map_to_driver[ui->ifc_num++] = i;
- }
- }
- @@ -1645,38 +1808,15 @@ static int usb_find_descriptor(struct us
- if ((type == USB_DT_CONFIG) && (id == 0)) {
- struct usb_config_descriptor cfg;
- unsigned ifc_count = 0;
- - unsigned n;
- char *ptr, *start;
- - int max_packet;
- - if (ui->speed == USB_SPEED_HIGH)
- - max_packet = 512;
- - else
- - max_packet = 64;
- start = req->buf;
- ptr = start + USB_DT_CONFIG_SIZE;
- for (i = 0; i < ui->num_funcs; i++) {
- struct usb_function_info *fi = ui->func[i];
- -
- - /* check to see if the function was enabled when we
- - ** received the USB_DT_DEVICE request to make ensure
- - ** that the interfaces we return here match the
- - ** product ID we returned in the USB_DT_DEVICE.
- - */
- - if (enabled_functions & (1 << i)) {
- - ifc_count++;
- -
- - memcpy(ptr, &fi->ifc, fi->ifc.bLength);
- - ptr += fi->ifc.bLength;
- -
- - for (n = 0; n < fi->endpoints; n++) {
- - /* XXX hardcoded bulk */
- - fi->ept[n].desc.wMaxPacketSize = max_packet;
- - memcpy(ptr, &(fi->ept[n].desc), fi->ept[n].desc.bLength);
- - ptr += fi->ept[n].desc.bLength;
- - }
- - }
- + ptr = copy_ifc_descriptors(ptr, SETUP_BUF_SIZE - (ptr - start), ui, i);
- + ifc_count += fi->func->ifc_num ? fi->func->ifc_num : 1;
- }
- cfg.bLength = USB_DT_CONFIG_SIZE;
- @@ -1703,7 +1843,7 @@ static int usb_find_descriptor(struct us
- /* return language ID */
- buffer[0] = 4;
- buffer[1] = USB_DT_STRING;
- - buffer[2] = LANGUAGE_ID;
- + buffer[2] = LANGUAGE_ID & 0xff;
- buffer[3] = LANGUAGE_ID >> 8;
- break;
- case STRING_SERIAL:
- @@ -1715,6 +1855,27 @@ static int usb_find_descriptor(struct us
- case STRING_MANUFACTURER:
- copy_string_descriptor(pdata->manufacturer_name, buffer);
- break;
- + default: {
- + int disabled = 0;
- + if (id >= STRING_DISABLED_START) {
- + id = id - STRING_DISABLED_START + STRING_ENABLED_START;
- + disabled = 1;
- + }
- +
- + if (id >= STRING_ENABLED_START) {
- + for (i = 0; i < ui->num_funcs; i++) {
- + struct usb_function_info *fi = ui->func[i];
- + if ((id - STRING_ENABLED_START) == fi->func->position_bit && fi->func->ifc_name) {
- + copy_string_descriptor(fi->func->ifc_name, buffer);
- + if (disabled) string_descriptor_append_disabled(buffer);
- + break;
- + }
- + }
- + break;
- + }
- +
- + break;
- + }
- }
- if (buffer[0]) {
- diff -Naurp a/drivers/usb/function/mtp_tunnel.c b/drivers/usb/function/mtp_tunnel.c
- --- a/drivers/usb/function/mtp_tunnel.c 1970-01-01 01:00:00.000000000 +0100
- +++ b/drivers/usb/function/mtp_tunnel.c 2009-12-18 23:16:10.748280575 +0100
- @@ -0,0 +1,862 @@
- +/* drivers/usb/function/mtp_tunnel.c
- + *
- + * Function Device for the Android ADB Protocol
- + *
- + * Copyright (C) 2007 Google, Inc.
- + * Author: Brian Swetland <swetland@google.com>
- + *
- + * This software is licensed under the terms of the GNU General Public
- + * License version 2, as published by the Free Software Foundation, and
- + * may be copied, distributed, and modified under those terms.
- + *
- + * This program is distributed in the hope that it will be useful,
- + * but WITHOUT ANY WARRANTY; without even the implied warranty of
- + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + * GNU General Public License for more details.
- + *
- + * base on ADB function, modify to MTP tunnel function
- + * by Anthony_Chang <anthony_chang@htc.com>
- + */
- +
- +#include <linux/init.h>
- +#include <linux/module.h>
- +#include <linux/kernel.h>
- +#include <linux/miscdevice.h>
- +#include <linux/fs.h>
- +#include <linux/device.h>
- +#include <linux/platform_device.h>
- +#include <linux/sched.h>
- +
- +#include <linux/wait.h>
- +#include <linux/list.h>
- +
- +#include <asm/atomic.h>
- +#include <asm/uaccess.h>
- +#include <mach/msm_hsusb.h>
- +
- +#include "usb_function.h"
- +#include <linux/usb/cdc.h>
- +
- +/* please refer: Documentation/ioctl-number.txt and Documentation/ioctl/
- + * and choice magic-number */
- +#define USB_MTP_IOC_MAGIC 0xFF
- +
- +#define USB_MTP_FUNC_IOC_CANCEL_REQUEST_SET _IOW(USB_MTP_IOC_MAGIC, 0x20, int)
- +#define USB_MTP_FUNC_IOC_CANCEL_REQUEST_GET _IOW(USB_MTP_IOC_MAGIC, 0x21, int)
- +#define USB_MTP_FUNC_IOC_GET_EXTENDED_EVENT_DATA_SET _IOW(USB_MTP_IOC_MAGIC, 0x22, int)
- +#define USB_MTP_FUNC_IOC_GET_EXTENDED_EVENT_DATA_GET _IOW(USB_MTP_IOC_MAGIC, 0x23, int)
- +#define USB_MTP_FUNC_IOC_DEVICE_RESET_REQUEST_SET _IOW(USB_MTP_IOC_MAGIC, 0x24, int)
- +#define USB_MTP_FUNC_IOC_DEVICE_RESET_REQUEST_GET _IOW(USB_MTP_IOC_MAGIC, 0x25, int)
- +#define USB_MTP_FUNC_IOC_GET_DEVICE_STATUS_SET _IOW(USB_MTP_IOC_MAGIC, 0x26, int)
- +#define USB_MTP_FUNC_IOC_GET_DEVICE_STATUS_GET _IOW(USB_MTP_IOC_MAGIC, 0x27, int)
- +
- +/* base on Annex. D in PIMA15740-2000 spec */
- +#define PIMA15740_CANCEL_REQUEST 0x64
- +#define PIMA15740_GET_EXTENDED_EVENT_DATA 0x65
- +#define PIMA15740_DEVICE_RESET_REQUEST 0x66
- +#define PIMA15740_GET_DEVICE_STATUS 0x67
- +
- +static u16 mtp_tunnel_status = 0xFF;
- +
- +#if 1
- +#define DBG(x...) do {} while (0)
- +#else
- +#define DBG(x...) printk(KERN_INFO x)
- +#endif
- +
- +/* allocate buffer size to: 16384 byte */
- +#define ALLOCATE_16K_BUFF
- +
- +#ifdef ALLOCATE_16K_BUFF
- +#define TXN_MAX 16384
- +#else
- +#define TXN_MAX 4096
- +
- +/* number of rx and tx requests to allocate */
- +#define RX_REQ_MAX 4
- +#define TX_REQ_MAX 4
- +#endif
- +
- +#define MTP_TUNNEL_FUNCTION_NAME "mtp_tunnel"
- +
- +struct device mtp_tunnel_dev;
- +
- +struct mtp_tunnel_context
- +{
- + int online;
- + int error;
- +
- + atomic_t read_excl;
- + atomic_t write_excl;
- + atomic_t open_excl;
- + atomic_t enable_open_excl;
- + atomic_t enable_read_excl;
- + atomic_t enable_ioctl_excl;
- + spinlock_t lock;
- +
- + struct usb_endpoint *out;
- + struct usb_endpoint *in;
- +
- + struct list_head tx_idle;
- + struct list_head rx_idle;
- + struct list_head rx_done;
- +
- + wait_queue_head_t read_wq;
- + wait_queue_head_t write_wq;
- +
- + /* the request we're currently reading from */
- + struct usb_request *read_req;
- + unsigned char *read_buf;
- + unsigned read_count;
- + int registered;
- + struct platform_device *pdev;
- + u8 ioctl_tmp[2];
- +};
- +
- +static struct mtp_tunnel_context _context;
- +
- +struct _mtp_specific_request {
- + //unsigned char request_specific_sequence;
- + unsigned char request_specific_num;
- + const char *request_specific_string;
- +};
- +/*
- +static struct _mtp_specific_request mtp_specific_request[] = {
- + { PIMA15740_CANCEL_REQUEST, "Cancel_Request" },
- + { PIMA15740_GET_EXTENDED_EVENT_DATA, "Get_Extented_Event_Data" },
- + { PIMA15740_DEVICE_RESET_REQUEST, "Device_Reset_Request" },
- + { PIMA15740_GET_DEVICE_STATUS, "Get_Device_Status" },
- + { 0xFF, NULL }
- +};
- +*/
- +static inline int _lock(atomic_t *excl)
- +{
- + if (atomic_inc_return(excl) == 1) {
- + return 0;
- + } else {
- + atomic_dec(excl);
- + return -1;
- + }
- +}
- +
- +static inline void _unlock(atomic_t *excl)
- +{
- + atomic_dec(excl);
- +}
- +
- +/* add a request to the tail of a list */
- +void static req_put(struct mtp_tunnel_context *ctxt, struct list_head *head, struct usb_request *req)
- +{
- + unsigned long flags;
- +
- + spin_lock_irqsave(&ctxt->lock, flags);
- + list_add_tail(&req->list, head);
- + spin_unlock_irqrestore(&ctxt->lock, flags);
- +}
- +
- +/* remove a request from the head of a list */
- +static struct usb_request *req_get(struct mtp_tunnel_context *ctxt, struct list_head *head)
- +{
- + unsigned long flags;
- + struct usb_request *req;
- +
- + spin_lock_irqsave(&ctxt->lock, flags);
- + if (list_empty(head)) {
- + req = 0;
- + } else {
- + req = list_first_entry(head, struct usb_request, list);
- + list_del(&req->list);
- + }
- + spin_unlock_irqrestore(&ctxt->lock, flags);
- + return req;
- +}
- +
- +static void mtp_tunnel_complete_in(struct usb_endpoint *ept, struct usb_request *req)
- +{
- + struct mtp_tunnel_context *ctxt = req->context;
- +
- + if (req->status != 0)
- + ctxt->error = 1;
- +
- + req_put(ctxt, &ctxt->tx_idle, req);
- +
- + wake_up(&ctxt->write_wq);
- +}
- +
- +static void mtp_tunnel_complete_out(struct usb_endpoint *ept, struct usb_request *req)
- +{
- + struct mtp_tunnel_context *ctxt = req->context;
- +
- + if (req->status != 0) {
- + ctxt->error = 1;
- + req_put(ctxt, &ctxt->rx_idle, req);
- + } else {
- + req_put(ctxt, &ctxt->rx_done, req);
- + }
- +
- + wake_up(&ctxt->read_wq);
- +}
- +
- +static ssize_t mtp_tunnel_read(struct file *fp, char __user *buf,
- + size_t count, loff_t *pos)
- +{
- + struct mtp_tunnel_context *ctxt = &_context;
- + struct usb_request *req;
- + int r = count, xfer;
- + int ret;
- +
- + DBG("mtp_tunnel_read(%d)\n", count);
- +
- + if (_lock(&ctxt->read_excl))
- + return -EBUSY;
- +
- + /* we will block until we're online */
- + while (!(ctxt->online || ctxt->error)) {
- + DBG("mtp_tunnel_read: waiting for online state\n");
- + ret = wait_event_interruptible(ctxt->read_wq, (ctxt->online || ctxt->error));
- + if (ret < 0) {
- + _unlock(&ctxt->read_excl);
- + return ret;
- + }
- + }
- +
- + while (count > 0) {
- + if (ctxt->error) {
- + r = -EIO;
- + break;
- + }
- +
- + /* if we have idle read requests, get them queued */
- + while ((req = req_get(ctxt, &ctxt->rx_idle))) {
- +requeue_req:
- + req->length = TXN_MAX;
- + ret = usb_ept_queue_xfer(ctxt->out, req);
- + if (ret < 0) {
- + DBG("mtp_tunnel_read: failed to queue req %p (%d)\n", req, ret);
- + r = -EIO;
- + ctxt->error = 1;
- + req_put(ctxt, &ctxt->rx_idle, req);
- + goto fail;
- + } else {
- + DBG("%s(): rx %p queue\n", __func__, req);
- + }
- + }
- +
- + /* if we have data pending, give it to userspace */
- + if (ctxt->read_count > 0) {
- + xfer = (ctxt->read_count < count) ? ctxt->read_count : count;
- +
- + if (copy_to_user(buf, ctxt->read_buf, xfer)) {
- + r = -EFAULT;
- + break;
- + }
- + ctxt->read_buf += xfer;
- + ctxt->read_count -= xfer;
- + buf += xfer;
- + count -= xfer;
- +
- + /* if we've emptied the buffer, release the request */
- + if (ctxt->read_count == 0) {
- + req_put(ctxt, &ctxt->rx_idle, ctxt->read_req);
- + ctxt->read_req = 0;
- + }
- + continue;
- + }
- +
- + /* wait for a request to complete */
- + req = 0;
- + ret = wait_event_interruptible(ctxt->read_wq,
- + ((req = req_get(ctxt, &ctxt->rx_done)) || ctxt->error));
- +
- + if (req != 0) {
- + /* if we got a 0-len one we need to put it back into
- + ** service. if we made it the current read req we'd
- + ** be stuck forever
- + */
- + if (req->actual == 0)
- + goto requeue_req;
- +
- + ctxt->read_req = req;
- + ctxt->read_count = req->actual;
- + ctxt->read_buf = req->buf;
- + DBG("%s(): rx %p %d\n", __func__, req, req->actual);
- + }
- +
- + if (ret < 0) {
- + r = ret;
- + break;
- + }
- + }
- +
- +fail:
- + _unlock(&ctxt->read_excl);
- + return r;
- +}
- +
- +static ssize_t mtp_tunnel_write(struct file *fp, const char __user *buf,
- + size_t count, loff_t *pos)
- +{
- + struct mtp_tunnel_context *ctxt = &_context;
- + struct usb_request *req = 0;
- + int r = count, xfer;
- + int ret;
- +
- + DBG("mtp_tunnel_write(%d)\n", count);
- +
- + if (_lock(&ctxt->write_excl))
- + return -EBUSY;
- +
- + while (count > 0) {
- + if (ctxt->error) {
- + r = -EIO;
- + break;
- + }
- +
- + /* get an idle tx request to use */
- + req = 0;
- + ret = wait_event_interruptible(ctxt->write_wq,
- + ((req = req_get(ctxt, &ctxt->tx_idle)) || ctxt->error));
- +
- + if (ret < 0) {
- + r = ret;
- + break;
- + }
- +
- + if (req != 0) {
- + xfer = count > TXN_MAX ? TXN_MAX : count;
- + if (copy_from_user(req->buf, buf, xfer)) {
- + r = -EFAULT;
- + break;
- + }
- +
- + req->length = xfer;
- + ret = usb_ept_queue_xfer(ctxt->in, req);
- + if (ret < 0) {
- + DBG("mtp_tunnel_write: xfer error %d\n", ret);
- + ctxt->error = 1;
- + r = -EIO;
- + break;
- + }
- +
- + buf += xfer;
- + count -= xfer;
- +
- + /* zero this so we don't try to free it on error exit */
- + req = 0;
- + }
- + }
- +
- +
- + if (req)
- + req_put(ctxt, &ctxt->tx_idle, req);
- +
- + _unlock(&ctxt->write_excl);
- + return r;
- +}
- +
- +static int mtp_tunnel_open(struct inode *ip, struct file *fp)
- +{
- + struct mtp_tunnel_context *ctxt = &_context;
- +
- + if (_lock(&ctxt->open_excl))
- + return -EBUSY;
- +
- + /* clear the error latch */
- + ctxt->error = 0;
- +
- + return 0;
- +}
- +
- +static int mtp_tunnel_release(struct inode *ip, struct file *fp)
- +{
- + struct mtp_tunnel_context *ctxt = &_context;
- +
- + _unlock(&ctxt->open_excl);
- + return 0;
- +}
- +
- +static struct file_operations mtp_tunnel_fops = {
- + .owner = THIS_MODULE,
- + .read = mtp_tunnel_read,
- + .write = mtp_tunnel_write,
- + .open = mtp_tunnel_open,
- + .release = mtp_tunnel_release,
- +};
- +
- +static struct miscdevice mtp_tunnel_device = {
- + .minor = MISC_DYNAMIC_MINOR,
- + .name = "android_mtp_tunnel",
- + .fops = &mtp_tunnel_fops,
- +};
- +
- +static DECLARE_WAIT_QUEUE_HEAD(mtp_tunnel_enable_read_wait);
- +#if 0
- +static ssize_t mtp_tunnel_enable_read(struct file *fp, char __user *buf,
- + size_t count, loff_t *pos)
- +{
- + struct mtp_tunnel_context *ctxt = &_context;
- + int r = 0;
- + int loop_i;
- +
- + DBG("mtp_tunnel_read(%d)\n", count);
- +
- + if (_lock(&ctxt->enable_read_excl))
- + return -EBUSY;
- +/*
- + while(mtp_tunnel_status == 0xFF) {
- + _unlock(&ctxt->enable_read_excl);
- + if(fp->f_flags & O_NONBLOCK) {
- + _unlock(&ctxt->enable_read_excl);
- + r = -EAGAIN;
- + goto fail;
- + }
- + if(wait_event_interruptible(mtp_tunnel_enable_read_wait,
- + mtp_tunnel_status != 0xFF)) {
- + r = -ERESTARTSYS;
- + goto fail;
- + }
- + if (_lock(&ctxt->enable_read_excl))
- + return -EBUSY;
- + }
- +*/
- + for(loop_i = 0; loop_i < ARRAY_SIZE(mtp_specific_request); loop_i++) {
- + if(mtp_tunnel_status == mtp_specific_request[loop_i].request_specific_sequence) {
- + if(mtp_specific_request[loop_i].request_specific_string != NULL)
- + r = strlen(mtp_specific_request[loop_i].request_specific_string);
- + if(r)
- + if(copy_to_user(buf,
- + mtp_specific_request[loop_i].request_specific_string,
- + r))
- + r = -EFAULT;
- + break;
- + }
- + }
- + count -= r;
- + *pos += r;
- +fail:
- + _unlock(&ctxt->enable_read_excl);
- + return r;
- +}
- +#endif
- +static int
- +mtp_tunnel_enable_ioctl(struct inode *inode, struct file *file,
- + unsigned int cmd, unsigned long arg)
- +{
- + struct mtp_tunnel_context *ctxt = &_context;
- + void __user *argp = (void __user *)arg;
- + int tmp_value;
- +
- + if (_lock(&ctxt->enable_ioctl_excl))
- + return -EBUSY;
- +
- + if(_IOC_TYPE(cmd) != USB_MTP_IOC_MAGIC) {
- + printk(KERN_NOTICE "_IOC_TYPE(cmd) != USB_MTP_IOC_MAGIC, return -ENOTTY\n");
- + _unlock(&ctxt->enable_ioctl_excl);
- + return -ENOTTY;
- + }
- +
- + switch(cmd) {
- + case USB_MTP_FUNC_IOC_CANCEL_REQUEST_SET:
- + printk(KERN_NOTICE "%s: USB_MTP_FUNC_IOC_CANCEL_REQUEST_SET\n", __func__);
- + mtp_tunnel_status = 0xFF;
- + break;
- + case USB_MTP_FUNC_IOC_GET_DEVICE_STATUS_SET:
- + printk(KERN_NOTICE "%s: USB_MTP_FUNC_IOC_GET_DEVICE_STATUS_SET\n", __func__);
- + if (copy_from_user(&tmp_value, argp, sizeof(int)))
- + return -EFAULT;
- + ctxt->ioctl_tmp[0] = 0xFF & tmp_value;
- + ctxt->ioctl_tmp[1] = 0xFF & (tmp_value >> 8);
- + printk(KERN_INFO "%s: data_0: 0x%X, data_1: 0x%X #\n\n\n",
- + __func__, ctxt->ioctl_tmp[0], ctxt->ioctl_tmp[1]);
- + mtp_tunnel_status = 0xFF;
- + break;
- + case USB_MTP_FUNC_IOC_GET_DEVICE_STATUS_GET:
- + printk(KERN_NOTICE "%s: USB_MTP_FUNC_IOC_GET_DEVICE_STATUS_GET\n", __func__);
- + break;
- +#if 0
- +/* Needless, return -ENOTTY */
- + case USB_MTP_FUNC_IOC_CANCEL_REQUEST_GET:
- + printk(KERN_NOTICE "%s: USB_MTP_FUNC_IOC_CANCEL_REQUEST_GET\n", __func__);
- + return -ENOTTY;
- + break;
- + case USB_MTP_FUNC_IOC_GET_EXTENDED_EVENT_DATA_SET:
- + printk(KERN_NOTICE "%s: USB_MTP_FUNC_IOC_GET_EXTENDED_EVENT_DATA_SET\n", __func__);
- + return -ENOTTY;
- + break;
- + case USB_MTP_FUNC_IOC_GET_EXTENDED_EVENT_DATA_GET:
- + printk(KERN_NOTICE "%s: USB_MTP_FUNC_IOC_GET_EXTENDED_EVENT_DATA_GET\n", __func__);
- + return -ENOTTY;
- + break;
- + case USB_MTP_FUNC_IOC_DEVICE_RESET_REQUEST_SET:
- + printk(KERN_NOTICE "%s: USB_MTP_FUNC_IOC_DEVICE_RESET_REQUEST_SET\n", __func__);
- + return -ENOTTY;
- + break;
- + case USB_MTP_FUNC_IOC_DEVICE_RESET_REQUEST_GET:
- + printk(KERN_NOTICE "%s: USB_MTP_FUNC_IOC_DEVICE_RESET_REQUEST_GET\n", __func__);
- + return -ENOTTY;
- + break;
- +#endif
- + default:
- + printk(KERN_NOTICE "%s: default, will return -ENOTTY\n", __func__);
- + return -ENOTTY;
- + break;
- + }
- + _unlock(&ctxt->enable_ioctl_excl);
- + return 0;
- +}
- +
- +static int mtp_tunnel_enable_open(struct inode *ip, struct file *fp)
- +{
- + struct mtp_tunnel_context *ctxt = &_context;
- +
- + if (_lock(&ctxt->enable_open_excl))
- + return -EBUSY;
- +
- + DBG("%s(): Enabling %s function ###\n", __func__, MTP_TUNNEL_FUNCTION_NAME);
- + usb_function_enable(MTP_TUNNEL_FUNCTION_NAME, 1);
- + /* clear the error latch */
- + ctxt->error = 0;
- +
- + return 0;
- +}
- +
- +static int mtp_tunnel_enable_release(struct inode *ip, struct file *fp)
- +{
- + struct mtp_tunnel_context *ctxt = &_context;
- +
- + DBG("%s(): Disabling %s function ###\n", __func__, MTP_TUNNEL_FUNCTION_NAME);
- + usb_function_enable(MTP_TUNNEL_FUNCTION_NAME, 0);
- + _unlock(&ctxt->enable_open_excl);
- + return 0;
- +}
- +
- +
- +static struct file_operations mtp_tunnel_fops_enable = {
- + .owner = THIS_MODULE,
- + //.read = mtp_tunnel_enable_read,
- + .open = mtp_tunnel_enable_open,
- + .ioctl = mtp_tunnel_enable_ioctl,
- + .release = mtp_tunnel_enable_release,
- +};
- +
- +static struct miscdevice mtp_tunnel_enable_device = {
- + .minor = MISC_DYNAMIC_MINOR,
- + .name = "android_mtp_enable",
- + .fops = &mtp_tunnel_fops_enable,
- +};
- +
- +static ssize_t store_mtp_tunnel_status(struct device *dev,
- + struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- + u32 ul;
- + struct mtp_tunnel_context *ctxt = &_context;
- +
- + ul = simple_strtoul(buf, NULL, 10);
- + if(ul != PIMA15740_CANCEL_REQUEST && ul != PIMA15740_GET_DEVICE_STATUS) {
- + printk(KERN_WARNING "Unknown mtp_tunnel status: 0x%2.2X\n", ul);
- + return -EINVAL;
- + }
- + mtp_tunnel_status = ul;
- + kobject_uevent(&ctxt->pdev->dev.kobj, KOBJ_CHANGE);
- +
- + return count;
- +}
- +
- +static ssize_t show_mtp_tunnel_status(struct device *dev,
- + struct device_attribute *attr,
- + char *buf)
- +{
- + int rc = 0;
- +
- + switch(mtp_tunnel_status) {
- + case PIMA15740_CANCEL_REQUEST:
- + rc += sprintf(buf, "Cancel_Request\n");
- + break;
- + case PIMA15740_GET_DEVICE_STATUS:
- + rc += sprintf(buf, "Get_Device_Status\n");
- + break;
- + }
- + return rc;
- +}
- +
- +static DEVICE_ATTR(mtp_tunnel_status, 0644, show_mtp_tunnel_status, store_mtp_tunnel_status);
- +
- +static void mtp_tunnel_dev_release (struct device *dev) {}
- +
- +static int mtp_tunnel_probe_register (struct platform_device *pdev)
- +{
- + struct mtp_tunnel_context *ctxt = &_context;
- + ctxt->pdev = pdev;
- + return 0;
- +}
- +
- +static struct platform_driver mtp_tunnel_driver = {
- + .probe = mtp_tunnel_probe_register,
- + .driver = { .name = MTP_TUNNEL_FUNCTION_NAME, },
- +};
- +
- +static void mtp_tunnel_release_register (struct device *dev) {}
- +
- +static struct platform_device mtp_tunnel_device_register = {
- + .name = MTP_TUNNEL_FUNCTION_NAME,
- + .id = -1,
- + .dev = {
- + .release = mtp_tunnel_release_register,
- + },
- +};
- +
- +static void mtp_tunnel_unbind(void *_ctxt)
- +{
- + struct mtp_tunnel_context *ctxt = _ctxt;
- + struct usb_request *req;
- +
- + printk(KERN_DEBUG "mtp_tunnel_unbind()\n");
- +
- + while ((req = req_get(ctxt, &ctxt->rx_idle))) {
- + usb_ept_free_req(ctxt->out, req);
- + }
- + while ((req = req_get(ctxt, &ctxt->tx_idle))) {
- + usb_ept_free_req(ctxt->in, req);
- + }
- +
- + ctxt->online = 0;
- + ctxt->error = 1;
- +
- + if (ctxt->registered) {
- + device_remove_file(&mtp_tunnel_dev, &dev_attr_mtp_tunnel_status);
- + device_unregister(&mtp_tunnel_dev);
- + ctxt->registered = 0;
- + }
- +
- + /* readers may be blocked waiting for us to go online */
- + wake_up(&ctxt->read_wq);
- +}
- +
- +static void mtp_tunnel_bind(struct usb_endpoint **ept, void *_ctxt)
- +{
- + struct mtp_tunnel_context *ctxt = _ctxt;
- + struct usb_request *req;
- + int ret;
- +#ifndef ALLOCATE_16K_BUFF
- + int n;
- +#endif
- + ctxt->registered = 0;
- + ctxt->out = ept[0];
- + ctxt->in = ept[1];
- +
- + printk(KERN_DEBUG "mtp_tunnel_bind() %p, %p\n", ctxt->out, ctxt->in);
- +
- +#ifndef ALLOCATE_16K_BUFF
- + for (n = 0; n < RX_REQ_MAX; n++)
- +#endif
- + {
- + req = usb_ept_alloc_req(ctxt->out, TXN_MAX);
- + if (req == 0) goto fail;
- + req->context = ctxt;
- + req->complete = mtp_tunnel_complete_out;
- + req_put(ctxt, &ctxt->rx_idle, req);
- + }
- +
- +
- +#ifndef ALLOCATE_16K_BUFF
- + for (n = 0; n < TX_REQ_MAX; n++)
- +#endif
- + {
- + req = usb_ept_alloc_req(ctxt->in, TXN_MAX);
- + if (req == 0) goto fail;
- + req->context = ctxt;
- + req->complete = mtp_tunnel_complete_in;
- + req_put(ctxt, &ctxt->tx_idle, req);
- + }
- +
- +#ifndef ALLOCATE_16K_BUFF
- + printk(KERN_DEBUG
- + "mtp_tunnel_bind() allocated %d rx and %d tx requests\n",
- + RX_REQ_MAX, TX_REQ_MAX);
- +#else
- + printk(KERN_DEBUG
- + "%s(): allocated buffer: %d\n", __func__, TXN_MAX);
- +#endif
- +
- + misc_register(&mtp_tunnel_device);
- + misc_register(&mtp_tunnel_enable_device);
- +
- + mtp_tunnel_dev.release = mtp_tunnel_dev_release;
- + mtp_tunnel_dev.parent = &ctxt->pdev->dev;
- + strcpy(mtp_tunnel_dev.bus_id, "interface");
- +
- + ret = device_register(&mtp_tunnel_dev);
- + if (ret != 0) {
- + printk(KERN_WARNING "mtp_tunnel_dev failed to register device: %d\n", ret);
- + goto fail_dev_register_fail;
- + }
- + ret = device_create_file(&mtp_tunnel_dev, &dev_attr_mtp_tunnel_status);
- + if (ret != 0) {
- + printk(KERN_WARNING "mtp_tunnel_dev device_create_file failed: %d\n", ret);
- + device_unregister(&mtp_tunnel_dev);
- + goto fail_dev_register_fail;
- + }
- + ctxt->registered = 1;
- + return;
- +
- +fail_dev_register_fail:
- + printk(KERN_ERR "%s() could not allocate requests\n", __func__);
- +
- +fail:
- + printk(KERN_WARNING "mtp_tunnel_bind() could not allocate requests\n");
- + mtp_tunnel_unbind(ctxt);
- +}
- +
- +static void mtp_tunnel_configure(int configured, void *_ctxt)
- +{
- + struct mtp_tunnel_context *ctxt = _ctxt;
- + struct usb_request *req;
- +
- + DBG("mtp_tunnel_configure() %d\n", configured);
- +
- + if (configured) {
- + ctxt->online = 1;
- +
- + /* if we have a stale request being read, recycle it */
- + ctxt->read_buf = 0;
- + ctxt->read_count = 0;
- + if (ctxt->read_req) {
- + req_put(ctxt, &ctxt->rx_idle, ctxt->read_req);
- + ctxt->read_req = 0;
- + }
- +
- + /* retire any completed rx requests from previous session */
- + while ((req = req_get(ctxt, &ctxt->rx_done)))
- + req_put(ctxt, &ctxt->rx_idle, req);
- +
- + } else {
- + ctxt->online = 0;
- + ctxt->error = 1;
- + }
- +
- + /* readers may be blocked waiting for us to go online */
- + wake_up(&ctxt->read_wq);
- +}
- +
- +static int mtp_tunnel_setup(struct usb_ctrlrequest *ctrl, void *buf,
- + int len, void *_ctxt)
- +{
- + struct mtp_tunnel_context *ctxt = _ctxt;
- + int value = -EOPNOTSUPP;
- + u16 w_index = le16_to_cpu(ctrl->wIndex);
- + //u16 w_value = le16_to_cpu(ctrl->wValue);
- + u16 w_length = le16_to_cpu(ctrl->wLength);
- +
- + switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
- + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
- + | USB_CDC_REQ_SET_CONTROL_LINE_STATE:
- + printk(KERN_DEBUG "%s(): USB_CDC_REQ_SET_CONTROL_LINE_STATE\n", __func__);
- + value = 1;
- + break;
- + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
- + | PIMA15740_CANCEL_REQUEST:
- + printk(KERN_DEBUG "%s(): PIMA15740_CANCEL_REQUEST\n", __func__);
- + if(w_length != 6)
- + break;
- + mtp_tunnel_status = PIMA15740_CANCEL_REQUEST;
- + kobject_uevent(&ctxt->pdev->dev.kobj, KOBJ_CHANGE);
- + // wake_up_interruptible(&mtp_tunnel_enable_read_wait);
- + if(wait_event_interruptible_timeout(mtp_tunnel_enable_read_wait,
- + mtp_tunnel_status == 0xFF, HZ * 5))
- + value = -ERESTARTSYS;
- + value = 0;
- + break;
- + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
- + | PIMA15740_GET_DEVICE_STATUS:
- + printk(KERN_DEBUG "%s(): PIMA15740_GET_DEVICE_STATUS(%d)\n", __func__, w_length);
- + value = w_length;
- + mtp_tunnel_status = PIMA15740_GET_DEVICE_STATUS;
- + kobject_uevent(&ctxt->pdev->dev.kobj, KOBJ_CHANGE);
- + //wake_up_interruptible(&mtp_tunnel_enable_read_wait);
- + if(wait_event_interruptible_timeout(mtp_tunnel_enable_read_wait,
- + mtp_tunnel_status == 0xFF, HZ * 5)) {
- + value = -ERESTARTSYS;
- + } else {
- + printk(KERN_INFO "%s: prepare data for send back to PC\n", __func__);
- + *(u8 *)buf = ctxt->ioctl_tmp[0];
- + *(u8 *)(buf+1) = ctxt->ioctl_tmp[1];
- + value = 2;
- + }
- + break;
- +
- + }
- + if (value == -EOPNOTSUPP)
- + printk(KERN_ERR
- + "%s: unknown class-specific control req "
- + "%02x.%02x v%04x i%04x l%u\n", __func__,
- + ctrl->bRequestType, ctrl->bRequest,
- + le16_to_cpu(ctrl->wValue), w_index, w_length);
- +
- + if (value == -ERESTARTSYS)
- + printk(KERN_ERR
- + "%s: wain_event_interruptible fail...(0x%X)\n", __func__, ctrl->bRequest);
- + return value;
- +}
- +
- +static struct usb_function usb_func_mtp_tunnel = {
- + .bind = mtp_tunnel_bind,
- + .unbind = mtp_tunnel_unbind,
- + .configure = mtp_tunnel_configure,
- + .setup = mtp_tunnel_setup,
- +
- + .name = MTP_TUNNEL_FUNCTION_NAME,
- + .context = &_context,
- +
- + .ifc_class = 0xFF,
- + .ifc_subclass = 0xFF,
- + .ifc_protocol = 0xFF,
- +
- + .ifc_name = MTP_TUNNEL_FUNCTION_NAME,
- +
- + .ifc_ept_count = 2,
- + .ifc_ept_type = { EPT_BULK_OUT, EPT_BULK_IN },
- +
- + /* the mtp_tunnel function is only enabled when its driver file is open */
- + .disabled = 1,
- + .position_bit = USB_FUNCTION_MTP_TUNNEL_NUM,
- + .ifc_num = 1,
- +};
- +
- +static int __init mtp_tunnel_init(void)
- +{
- + struct mtp_tunnel_context *ctxt = &_context;
- + int retval;
- + DBG("mtp_tunnel_init()\n");
- +
- + init_waitqueue_head(&ctxt->read_wq);
- + init_waitqueue_head(&ctxt->write_wq);
- +
- + atomic_set(&ctxt->open_excl, 0);
- + atomic_set(&ctxt->read_excl, 0);
- + atomic_set(&ctxt->write_excl, 0);
- +
- + atomic_set(&ctxt->enable_open_excl, 0);
- + atomic_set(&ctxt->enable_read_excl, 0);
- + atomic_set(&ctxt->enable_ioctl_excl, 0);
- +
- + spin_lock_init(&ctxt->lock);
- +
- + INIT_LIST_HEAD(&ctxt->rx_idle);
- + INIT_LIST_HEAD(&ctxt->rx_done);
- + INIT_LIST_HEAD(&ctxt->tx_idle);
- + retval = platform_driver_register (&mtp_tunnel_driver);
- + if (retval < 0)
- + return retval;
- + retval = platform_device_register (&mtp_tunnel_device_register);
- + if (retval < 0)
- + goto err_register_device;
- +
- + return usb_function_register(&usb_func_mtp_tunnel);
- +
- +err_register_device:
- + platform_driver_unregister(&mtp_tunnel_driver);
- + return retval;
- +}
- +
- +module_init(mtp_tunnel_init);
- diff -Naurp a/drivers/usb/function/ndis.h b/drivers/usb/function/ndis.h
- --- a/drivers/usb/function/ndis.h 1970-01-01 01:00:00.000000000 +0100
- +++ b/drivers/usb/function/ndis.h 2009-12-18 23:16:35.274489178 +0100
- @@ -0,0 +1,217 @@
- +/*
- + * ndis.h
- + *
- + * ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de>
- + *
- + * Thanks to the cygwin development team,
- + * espacially to Casper S. Hornstrup <chorns@users.sourceforge.net>
- + *
- + * THIS SOFTWARE IS NOT COPYRIGHTED
- + *
- + * This source code is offered for use in the public domain. You may
- + * use, modify or distribute it freely.
- + *
- + * This code is distributed in the hope that it will be useful but
- + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
- + * DISCLAIMED. This includes but is not limited to warranties of
- + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- + *
- + */
- +
- +#ifndef _LINUX_NDIS_H
- +#define _LINUX_NDIS_H
- +
- +
- +#define NDIS_STATUS_MULTICAST_FULL 0xC0010009
- +#define NDIS_STATUS_MULTICAST_EXISTS 0xC001000A
- +#define NDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B
- +
- +enum NDIS_DEVICE_POWER_STATE {
- + NdisDeviceStateUnspecified = 0,
- + NdisDeviceStateD0,
- + NdisDeviceStateD1,
- + NdisDeviceStateD2,
- + NdisDeviceStateD3,
- + NdisDeviceStateMaximum
- +};
- +
- +struct NDIS_PM_WAKE_UP_CAPABILITIES {
- + enum NDIS_DEVICE_POWER_STATE MinMagicPacketWakeUp;
- + enum NDIS_DEVICE_POWER_STATE MinPatternWakeUp;
- + enum NDIS_DEVICE_POWER_STATE MinLinkChangeWakeUp;
- +};
- +
- +/* NDIS_PNP_CAPABILITIES.Flags constants */
- +#define NDIS_DEVICE_WAKE_UP_ENABLE 0x00000001
- +#define NDIS_DEVICE_WAKE_ON_PATTERN_MATCH_ENABLE 0x00000002
- +#define NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE 0x00000004
- +
- +struct NDIS_PNP_CAPABILITIES {
- + __le32 Flags;
- + struct NDIS_PM_WAKE_UP_CAPABILITIES WakeUpCapabilities;
- +};
- +
- +struct NDIS_PM_PACKET_PATTERN {
- + __le32 Priority;
- + __le32 Reserved;
- + __le32 MaskSize;
- + __le32 PatternOffset;
- + __le32 PatternSize;
- + __le32 PatternFlags;
- +};
- +
- +
- +/* Required Object IDs (OIDs) */
- +#define OID_GEN_SUPPORTED_LIST 0x00010101
- +#define OID_GEN_HARDWARE_STATUS 0x00010102
- +#define OID_GEN_MEDIA_SUPPORTED 0x00010103
- +#define OID_GEN_MEDIA_IN_USE 0x00010104
- +#define OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105
- +#define OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106
- +#define OID_GEN_LINK_SPEED 0x00010107
- +#define OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108
- +#define OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109
- +#define OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A
- +#define OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B
- +#define OID_GEN_VENDOR_ID 0x0001010C
- +#define OID_GEN_VENDOR_DESCRIPTION 0x0001010D
- +#define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E
- +#define OID_GEN_CURRENT_LOOKAHEAD 0x0001010F
- +#define OID_GEN_DRIVER_VERSION 0x00010110
- +#define OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111
- +#define OID_GEN_PROTOCOL_OPTIONS 0x00010112
- +#define OID_GEN_MAC_OPTIONS 0x00010113
- +#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114
- +#define OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115
- +#define OID_GEN_VENDOR_DRIVER_VERSION 0x00010116
- +#define OID_GEN_SUPPORTED_GUIDS 0x00010117
- +#define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118
- +#define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119
- +#define OID_GEN_MACHINE_NAME 0x0001021A
- +#define OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B
- +#define OID_GEN_VLAN_ID 0x0001021C
- +
- +/* Optional OIDs */
- +#define OID_GEN_MEDIA_CAPABILITIES 0x00010201
- +#define OID_GEN_PHYSICAL_MEDIUM 0x00010202
- +
- +/* Required statistics OIDs */
- +#define OID_GEN_XMIT_OK 0x00020101
- +#define OID_GEN_RCV_OK 0x00020102
- +#define OID_GEN_XMIT_ERROR 0x00020103
- +#define OID_GEN_RCV_ERROR 0x00020104
- +#define OID_GEN_RCV_NO_BUFFER 0x00020105
- +
- +/* Optional statistics OIDs */
- +#define OID_GEN_DIRECTED_BYTES_XMIT 0x00020201
- +#define OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202
- +#define OID_GEN_MULTICAST_BYTES_XMIT 0x00020203
- +#define OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204
- +#define OID_GEN_BROADCAST_BYTES_XMIT 0x00020205
- +#define OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206
- +#define OID_GEN_DIRECTED_BYTES_RCV 0x00020207
- +#define OID_GEN_DIRECTED_FRAMES_RCV 0x00020208
- +#define OID_GEN_MULTICAST_BYTES_RCV 0x00020209
- +#define OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A
- +#define OID_GEN_BROADCAST_BYTES_RCV 0x0002020B
- +#define OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C
- +#define OID_GEN_RCV_CRC_ERROR 0x0002020D
- +#define OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E
- +#define OID_GEN_GET_TIME_CAPS 0x0002020F
- +#define OID_GEN_GET_NETCARD_TIME 0x00020210
- +#define OID_GEN_NETCARD_LOAD 0x00020211
- +#define OID_GEN_DEVICE_PROFILE 0x00020212
- +#define OID_GEN_INIT_TIME_MS 0x00020213
- +#define OID_GEN_RESET_COUNTS 0x00020214
- +#define OID_GEN_MEDIA_SENSE_COUNTS 0x00020215
- +#define OID_GEN_FRIENDLY_NAME 0x00020216
- +#define OID_GEN_MINIPORT_INFO 0x00020217
- +#define OID_GEN_RESET_VERIFY_PARAMETERS 0x00020218
- +
- +/* IEEE 802.3 (Ethernet) OIDs */
- +#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001
- +
- +#define OID_802_3_PERMANENT_ADDRESS 0x01010101
- +#define OID_802_3_CURRENT_ADDRESS 0x01010102
- +#define OID_802_3_MULTICAST_LIST 0x01010103
- +#define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104
- +#define OID_802_3_MAC_OPTIONS 0x01010105
- +#define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101
- +#define OID_802_3_XMIT_ONE_COLLISION 0x01020102
- +#define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103
- +#define OID_802_3_XMIT_DEFERRED 0x01020201
- +#define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202
- +#define OID_802_3_RCV_OVERRUN 0x01020203
- +#define OID_802_3_XMIT_UNDERRUN 0x01020204
- +#define OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205
- +#define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206
- +#define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207
- +
- +/* OID_GEN_MINIPORT_INFO constants */
- +#define NDIS_MINIPORT_BUS_MASTER 0x00000001
- +#define NDIS_MINIPORT_WDM_DRIVER 0x00000002
- +#define NDIS_MINIPORT_SG_LIST 0x00000004
- +#define NDIS_MINIPORT_SUPPORTS_MEDIA_QUERY 0x00000008
- +#define NDIS_MINIPORT_INDICATES_PACKETS 0x00000010
- +#define NDIS_MINIPORT_IGNORE_PACKET_QUEUE 0x00000020
- +#define NDIS_MINIPORT_IGNORE_REQUEST_QUEUE 0x00000040
- +#define NDIS_MINIPORT_IGNORE_TOKEN_RING_ERRORS 0x00000080
- +#define NDIS_MINIPORT_INTERMEDIATE_DRIVER 0x00000100
- +#define NDIS_MINIPORT_IS_NDIS_5 0x00000200
- +#define NDIS_MINIPORT_IS_CO 0x00000400
- +#define NDIS_MINIPORT_DESERIALIZE 0x00000800
- +#define NDIS_MINIPORT_REQUIRES_MEDIA_POLLING 0x00001000
- +#define NDIS_MINIPORT_SUPPORTS_MEDIA_SENSE 0x00002000
- +#define NDIS_MINIPORT_NETBOOT_CARD 0x00004000
- +#define NDIS_MINIPORT_PM_SUPPORTED 0x00008000
- +#define NDIS_MINIPORT_SUPPORTS_MAC_ADDRESS_OVERWRITE 0x00010000
- +#define NDIS_MINIPORT_USES_SAFE_BUFFER_APIS 0x00020000
- +#define NDIS_MINIPORT_HIDDEN 0x00040000
- +#define NDIS_MINIPORT_SWENUM 0x00080000
- +#define NDIS_MINIPORT_SURPRISE_REMOVE_OK 0x00100000
- +#define NDIS_MINIPORT_NO_HALT_ON_SUSPEND 0x00200000
- +#define NDIS_MINIPORT_HARDWARE_DEVICE 0x00400000
- +#define NDIS_MINIPORT_SUPPORTS_CANCEL_SEND_PACKETS 0x00800000
- +#define NDIS_MINIPORT_64BITS_DMA 0x01000000
- +
- +#define NDIS_MEDIUM_802_3 0x00000000
- +#define NDIS_MEDIUM_802_5 0x00000001
- +#define NDIS_MEDIUM_FDDI 0x00000002
- +#define NDIS_MEDIUM_WAN 0x00000003
- +#define NDIS_MEDIUM_LOCAL_TALK 0x00000004
- +#define NDIS_MEDIUM_DIX 0x00000005
- +#define NDIS_MEDIUM_ARCENT_RAW 0x00000006
- +#define NDIS_MEDIUM_ARCENT_878_2 0x00000007
- +#define NDIS_MEDIUM_ATM 0x00000008
- +#define NDIS_MEDIUM_WIRELESS_LAN 0x00000009
- +#define NDIS_MEDIUM_IRDA 0x0000000A
- +#define NDIS_MEDIUM_BPC 0x0000000B
- +#define NDIS_MEDIUM_CO_WAN 0x0000000C
- +#define NDIS_MEDIUM_1394 0x0000000D
- +
- +#define NDIS_PACKET_TYPE_DIRECTED 0x00000001
- +#define NDIS_PACKET_TYPE_MULTICAST 0x00000002
- +#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004
- +#define NDIS_PACKET_TYPE_BROADCAST 0x00000008
- +#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010
- +#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020
- +#define NDIS_PACKET_TYPE_SMT 0x00000040
- +#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080
- +#define NDIS_PACKET_TYPE_GROUP 0x00000100
- +#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200
- +#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400
- +#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800
- +
- +#define NDIS_MEDIA_STATE_CONNECTED 0x00000000
- +#define NDIS_MEDIA_STATE_DISCONNECTED 0x00000001
- +
- +#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001
- +#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002
- +#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004
- +#define NDIS_MAC_OPTION_NO_LOOPBACK 0x00000008
- +#define NDIS_MAC_OPTION_FULL_DUPLEX 0x00000010
- +#define NDIS_MAC_OPTION_EOTX_INDICATION 0x00000020
- +#define NDIS_MAC_OPTION_8021P_PRIORITY 0x00000040
- +#define NDIS_MAC_OPTION_RESERVED 0x80000000
- +
- +#endif /* _LINUX_NDIS_H */
- diff -Naurp a/drivers/usb/function/null.c b/drivers/usb/function/null.c
- --- a/drivers/usb/function/null.c 2009-12-18 22:10:56.331465000 +0100
- +++ b/drivers/usb/function/null.c 2009-12-18 22:11:02.699472440 +0100
- @@ -106,6 +106,8 @@ static struct usb_function usb_func_null
- .ifc_ept_count = 1,
- .ifc_ept_type = { EPT_BULK_OUT },
- +
- + .position_bit = USB_FUNCTION_NULL_NUM,
- };
- static int __init null_init(void)
- diff -Naurp a/drivers/usb/function/rndis.c b/drivers/usb/function/rndis.c
- --- a/drivers/usb/function/rndis.c 1970-01-01 01:00:00.000000000 +0100
- +++ b/drivers/usb/function/rndis.c 2009-12-18 23:16:54.619476954 +0100
- @@ -0,0 +1,1455 @@
- +/*
- + * RNDIS MSG parser
- + *
- + * Version: $Id: rndis.c,v 1.19 2004/03/25 21:33:46 robert Exp $
- + *
- + * Authors: Benedikt Spranger, Pengutronix
- + * Robert Schwebel, Pengutronix
- + *
- + * This program is free software; you can redistribute it and/or
- + * modify it under the terms of the GNU General Public License
- + * version 2, as published by the Free Software Foundation.
- + *
- + * This software was originally developed in conformance with
- + * Microsoft's Remote NDIS Specification License Agreement.
- + *
- + * 03/12/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
- + * Fixed message length bug in init_response
- + *
- + * 03/25/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
- + * Fixed rndis_rm_hdr length bug.
- + *
- + * Copyright (C) 2004 by David Brownell
- + * updates to merge with Linux 2.6, better match RNDIS spec
- + */
- +
- +#include <linux/module.h>
- +#include <linux/moduleparam.h>
- +#include <linux/kernel.h>
- +#include <linux/errno.h>
- +#include <linux/init.h>
- +#include <linux/list.h>
- +#include <linux/proc_fs.h>
- +#include <linux/netdevice.h>
- +
- +#include <asm/io.h>
- +#include <asm/byteorder.h>
- +#include <asm/system.h>
- +#include <asm/unaligned.h>
- +
- +
- +#undef RNDIS_PM
- +#undef RNDIS_WAKEUP
- +#undef VERBOSE
- +
- +#include "rndis.h"
- +
- +
- +/* The driver for your USB chip needs to support ep0 OUT to work with
- + * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional).
- + *
- + * Windows hosts need an INF file like Documentation/usb/linux.inf
- + * and will be happier if you provide the host_addr module parameter.
- + */
- +
- +#if 0
- +static int rndis_debug = 0;
- +module_param (rndis_debug, int, 0);
- +MODULE_PARM_DESC (rndis_debug, "enable debugging");
- +#else
- +#define rndis_debug 0
- +#endif
- +
- +#define DBG(str,args...) do { \
- + if (rndis_debug) \
- + pr_debug(str , ## args); \
- + } while (0)
- +
- +#define RNDIS_MAX_CONFIGS 1
- +
- +
- +static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS];
- +
- +/* Driver Version */
- +static const __le32 rndis_driver_version = __constant_cpu_to_le32 (1);
- +
- +/* Function Prototypes */
- +static rndis_resp_t *rndis_add_response (int configNr, u32 length);
- +
- +
- +/* supported OIDs */
- +static const u32 oid_supported_list [] =
- +{
- + /* the general stuff */
- + OID_GEN_SUPPORTED_LIST,
- + OID_GEN_HARDWARE_STATUS,
- + OID_GEN_MEDIA_SUPPORTED,
- + OID_GEN_MEDIA_IN_USE,
- + OID_GEN_MAXIMUM_FRAME_SIZE,
- + OID_GEN_LINK_SPEED,
- + OID_GEN_TRANSMIT_BLOCK_SIZE,
- + OID_GEN_RECEIVE_BLOCK_SIZE,
- + OID_GEN_VENDOR_ID,
- + OID_GEN_VENDOR_DESCRIPTION,
- + OID_GEN_VENDOR_DRIVER_VERSION,
- + OID_GEN_CURRENT_PACKET_FILTER,
- + OID_GEN_MAXIMUM_TOTAL_SIZE,
- + OID_GEN_MEDIA_CONNECT_STATUS,
- + OID_GEN_PHYSICAL_MEDIUM,
- +#if 0
- + OID_GEN_RNDIS_CONFIG_PARAMETER,
- +#endif
- +
- + /* the statistical stuff */
- + OID_GEN_XMIT_OK,
- + OID_GEN_RCV_OK,
- + OID_GEN_XMIT_ERROR,
- + OID_GEN_RCV_ERROR,
- + OID_GEN_RCV_NO_BUFFER,
- +#ifdef RNDIS_OPTIONAL_STATS
- + OID_GEN_DIRECTED_BYTES_XMIT,
- + OID_GEN_DIRECTED_FRAMES_XMIT,
- + OID_GEN_MULTICAST_BYTES_XMIT,
- + OID_GEN_MULTICAST_FRAMES_XMIT,
- + OID_GEN_BROADCAST_BYTES_XMIT,
- + OID_GEN_BROADCAST_FRAMES_XMIT,
- + OID_GEN_DIRECTED_BYTES_RCV,
- + OID_GEN_DIRECTED_FRAMES_RCV,
- + OID_GEN_MULTICAST_BYTES_RCV,
- + OID_GEN_MULTICAST_FRAMES_RCV,
- + OID_GEN_BROADCAST_BYTES_RCV,
- + OID_GEN_BROADCAST_FRAMES_RCV,
- + OID_GEN_RCV_CRC_ERROR,
- + OID_GEN_TRANSMIT_QUEUE_LENGTH,
- +#endif /* RNDIS_OPTIONAL_STATS */
- +
- + /* mandatory 802.3 */
- + /* the general stuff */
- + OID_802_3_PERMANENT_ADDRESS,
- + OID_802_3_CURRENT_ADDRESS,
- + OID_802_3_MULTICAST_LIST,
- + OID_802_3_MAC_OPTIONS,
- + OID_802_3_MAXIMUM_LIST_SIZE,
- +
- + /* the statistical stuff */
- + OID_802_3_RCV_ERROR_ALIGNMENT,
- + OID_802_3_XMIT_ONE_COLLISION,
- + OID_802_3_XMIT_MORE_COLLISIONS,
- +#ifdef RNDIS_OPTIONAL_STATS
- + OID_802_3_XMIT_DEFERRED,
- + OID_802_3_XMIT_MAX_COLLISIONS,
- + OID_802_3_RCV_OVERRUN,
- + OID_802_3_XMIT_UNDERRUN,
- + OID_802_3_XMIT_HEARTBEAT_FAILURE,
- + OID_802_3_XMIT_TIMES_CRS_LOST,
- + OID_802_3_XMIT_LATE_COLLISIONS,
- +#endif /* RNDIS_OPTIONAL_STATS */
- +
- +#ifdef RNDIS_PM
- + /* PM and wakeup are mandatory for USB: */
- +
- + /* power management */
- + OID_PNP_CAPABILITIES,
- + OID_PNP_QUERY_POWER,
- + OID_PNP_SET_POWER,
- +
- +#ifdef RNDIS_WAKEUP
- + /* wake up host */
- + OID_PNP_ENABLE_WAKE_UP,
- + OID_PNP_ADD_WAKE_UP_PATTERN,
- + OID_PNP_REMOVE_WAKE_UP_PATTERN,
- +#endif /* RNDIS_WAKEUP */
- +#endif /* RNDIS_PM */
- +};
- +
- +
- +/* NDIS Functions */
- +static int
- +gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
- + rndis_resp_t *r)
- +{
- + int retval = -ENOTSUPP;
- + u32 length = 4; /* usually */
- + __le32 *outbuf;
- + int i, count;
- + rndis_query_cmplt_type *resp;
- +
- + if (!r) return -ENOMEM;
- + resp = (rndis_query_cmplt_type *) r->buf;
- +
- + if (!resp) return -ENOMEM;
- +
- + if (buf_len && rndis_debug > 1) {
- + DBG("query OID %08x value, len %d:\n", OID, buf_len);
- + for (i = 0; i < buf_len; i += 16) {
- + DBG("%03d: %08x %08x %08x %08x\n", i,
- + le32_to_cpu(get_unaligned((__le32 *)
- + &buf[i])),
- + le32_to_cpu(get_unaligned((__le32 *)
- + &buf[i + 4])),
- + le32_to_cpu(get_unaligned((__le32 *)
- + &buf[i + 8])),
- + le32_to_cpu(get_unaligned((__le32 *)
- + &buf[i + 12])));
- + }
- + }
- +
- + /* response goes here, right after the header */
- + outbuf = (__le32 *) &resp[1];
- + resp->InformationBufferOffset = __constant_cpu_to_le32 (16);
- +
- + switch (OID) {
- +
- + /* general oids (table 4-1) */
- +
- + /* mandatory */
- + case OID_GEN_SUPPORTED_LIST:
- + DBG("%s: OID_GEN_SUPPORTED_LIST\n", __FUNCTION__);
- + length = sizeof (oid_supported_list);
- + count = length / sizeof (u32);
- + for (i = 0; i < count; i++)
- + outbuf[i] = cpu_to_le32 (oid_supported_list[i]);
- + retval = 0;
- + break;
- +
- + /* mandatory */
- + case OID_GEN_HARDWARE_STATUS:
- + DBG("%s: OID_GEN_HARDWARE_STATUS\n", __FUNCTION__);
- + /* Bogus question!
- + * Hardware must be ready to receive high level protocols.
- + * BTW:
- + * reddite ergo quae sunt Caesaris Caesari
- + * et quae sunt Dei Deo!
- + */
- + *outbuf = __constant_cpu_to_le32 (0);
- + retval = 0;
- + break;
- +
- + /* mandatory */
- + case OID_GEN_MEDIA_SUPPORTED:
- + DBG("%s: OID_GEN_MEDIA_SUPPORTED\n", __FUNCTION__);
- + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium);
- + retval = 0;
- + break;
- +
- + /* mandatory */
- + case OID_GEN_MEDIA_IN_USE:
- + DBG("%s: OID_GEN_MEDIA_IN_USE\n", __FUNCTION__);
- + /* one medium, one transport... (maybe you do it better) */
- + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium);
- + retval = 0;
- + break;
- +
- + /* mandatory */
- + case OID_GEN_MAXIMUM_FRAME_SIZE:
- + DBG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].dev) {
- + *outbuf = cpu_to_le32 (
- + rndis_per_dev_params [configNr].dev->mtu);
- + retval = 0;
- + }
- + break;
- +
- + /* mandatory */
- + case OID_GEN_LINK_SPEED:
- + if (rndis_debug > 1)
- + DBG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].media_state
- + == NDIS_MEDIA_STATE_DISCONNECTED)
- + *outbuf = __constant_cpu_to_le32 (0);
- + else
- + *outbuf = cpu_to_le32 (
- + rndis_per_dev_params [configNr].speed);
- + retval = 0;
- + break;
- +
- + /* mandatory */
- + case OID_GEN_TRANSMIT_BLOCK_SIZE:
- + DBG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].dev) {
- + *outbuf = cpu_to_le32 (
- + rndis_per_dev_params [configNr].dev->mtu);
- + retval = 0;
- + }
- + break;
- +
- + /* mandatory */
- + case OID_GEN_RECEIVE_BLOCK_SIZE:
- + DBG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].dev) {
- + *outbuf = cpu_to_le32 (
- + rndis_per_dev_params [configNr].dev->mtu);
- + retval = 0;
- + }
- + break;
- +
- + /* mandatory */
- + case OID_GEN_VENDOR_ID:
- + DBG("%s: OID_GEN_VENDOR_ID\n", __FUNCTION__);
- + *outbuf = cpu_to_le32 (
- + rndis_per_dev_params [configNr].vendorID);
- + retval = 0;
- + break;
- +
- + /* mandatory */
- + case OID_GEN_VENDOR_DESCRIPTION:
- + DBG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __FUNCTION__);
- + length = strlen (rndis_per_dev_params [configNr].vendorDescr);
- + memcpy (outbuf,
- + rndis_per_dev_params [configNr].vendorDescr, length);
- + retval = 0;
- + break;
- +
- + case OID_GEN_VENDOR_DRIVER_VERSION:
- + DBG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __FUNCTION__);
- + /* Created as LE */
- + *outbuf = rndis_driver_version;
- + retval = 0;
- + break;
- +
- + /* mandatory */
- + case OID_GEN_CURRENT_PACKET_FILTER:
- + DBG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__);
- + *outbuf = cpu_to_le32 (*rndis_per_dev_params[configNr].filter);
- + retval = 0;
- + break;
- +
- + /* mandatory */
- + case OID_GEN_MAXIMUM_TOTAL_SIZE:
- + DBG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __FUNCTION__);
- + *outbuf = __constant_cpu_to_le32(RNDIS_MAX_TOTAL_SIZE);
- + retval = 0;
- + break;
- +
- + /* mandatory */
- + case OID_GEN_MEDIA_CONNECT_STATUS:
- + if (rndis_debug > 1)
- + DBG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__);
- + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
- + .media_state);
- + retval = 0;
- + break;
- +
- + case OID_GEN_PHYSICAL_MEDIUM:
- + DBG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __FUNCTION__);
- + *outbuf = __constant_cpu_to_le32 (0);
- + retval = 0;
- + break;
- +
- + /* The RNDIS specification is incomplete/wrong. Some versions
- + * of MS-Windows expect OIDs that aren't specified there. Other
- + * versions emit undefined RNDIS messages. DOCUMENT ALL THESE!
- + */
- + case OID_GEN_MAC_OPTIONS: /* from WinME */
- + DBG("%s: OID_GEN_MAC_OPTIONS\n", __FUNCTION__);
- + *outbuf = __constant_cpu_to_le32(
- + NDIS_MAC_OPTION_RECEIVE_SERIALIZED
- + | NDIS_MAC_OPTION_FULL_DUPLEX);
- + retval = 0;
- + break;
- +
- + /* statistics OIDs (table 4-2) */
- +
- + /* mandatory */
- + case OID_GEN_XMIT_OK:
- + if (rndis_debug > 1)
- + DBG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].stats) {
- + *outbuf = cpu_to_le32 (
- + rndis_per_dev_params [configNr].stats->tx_packets -
- + rndis_per_dev_params [configNr].stats->tx_errors -
- + rndis_per_dev_params [configNr].stats->tx_dropped);
- + retval = 0;
- + }
- + break;
- +
- + /* mandatory */
- + case OID_GEN_RCV_OK:
- + if (rndis_debug > 1)
- + DBG("%s: OID_GEN_RCV_OK\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].stats) {
- + *outbuf = cpu_to_le32 (
- + rndis_per_dev_params [configNr].stats->rx_packets -
- + rndis_per_dev_params [configNr].stats->rx_errors -
- + rndis_per_dev_params [configNr].stats->rx_dropped);
- + retval = 0;
- + }
- + break;
- +
- + /* mandatory */
- + case OID_GEN_XMIT_ERROR:
- + if (rndis_debug > 1)
- + DBG("%s: OID_GEN_XMIT_ERROR\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].stats) {
- + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
- + .stats->tx_errors);
- + retval = 0;
- + }
- + break;
- +
- + /* mandatory */
- + case OID_GEN_RCV_ERROR:
- + if (rndis_debug > 1)
- + DBG("%s: OID_GEN_RCV_ERROR\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].stats) {
- + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
- + .stats->rx_errors);
- + retval = 0;
- + }
- + break;
- +
- + /* mandatory */
- + case OID_GEN_RCV_NO_BUFFER:
- + DBG("%s: OID_GEN_RCV_NO_BUFFER\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].stats) {
- + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
- + .stats->rx_dropped);
- + retval = 0;
- + }
- + break;
- +
- +#ifdef RNDIS_OPTIONAL_STATS
- + case OID_GEN_DIRECTED_BYTES_XMIT:
- + DBG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __FUNCTION__);
- + /*
- + * Aunt Tilly's size of shoes
- + * minus antarctica count of penguins
- + * divided by weight of Alpha Centauri
- + */
- + if (rndis_per_dev_params [configNr].stats) {
- + *outbuf = cpu_to_le32 (
- + (rndis_per_dev_params [configNr]
- + .stats->tx_packets -
- + rndis_per_dev_params [configNr]
- + .stats->tx_errors -
- + rndis_per_dev_params [configNr]
- + .stats->tx_dropped)
- + * 123);
- + retval = 0;
- + }
- + break;
- +
- + case OID_GEN_DIRECTED_FRAMES_XMIT:
- + DBG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __FUNCTION__);
- + /* dito */
- + if (rndis_per_dev_params [configNr].stats) {
- + *outbuf = cpu_to_le32 (
- + (rndis_per_dev_params [configNr]
- + .stats->tx_packets -
- + rndis_per_dev_params [configNr]
- + .stats->tx_errors -
- + rndis_per_dev_params [configNr]
- + .stats->tx_dropped)
- + / 123);
- + retval = 0;
- + }
- + break;
- +
- + case OID_GEN_MULTICAST_BYTES_XMIT:
- + DBG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].stats) {
- + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
- + .stats->multicast*1234);
- + retval = 0;
- + }
- + break;
- +
- + case OID_GEN_MULTICAST_FRAMES_XMIT:
- + DBG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].stats) {
- + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
- + .stats->multicast);
- + retval = 0;
- + }
- + break;
- +
- + case OID_GEN_BROADCAST_BYTES_XMIT:
- + DBG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].stats) {
- + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
- + .stats->tx_packets/42*255);
- + retval = 0;
- + }
- + break;
- +
- + case OID_GEN_BROADCAST_FRAMES_XMIT:
- + DBG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].stats) {
- + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
- + .stats->tx_packets/42);
- + retval = 0;
- + }
- + break;
- +
- + case OID_GEN_DIRECTED_BYTES_RCV:
- + DBG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __FUNCTION__);
- + *outbuf = __constant_cpu_to_le32 (0);
- + retval = 0;
- + break;
- +
- + case OID_GEN_DIRECTED_FRAMES_RCV:
- + DBG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __FUNCTION__);
- + *outbuf = __constant_cpu_to_le32 (0);
- + retval = 0;
- + break;
- +
- + case OID_GEN_MULTICAST_BYTES_RCV:
- + DBG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].stats) {
- + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
- + .stats->multicast * 1111);
- + retval = 0;
- + }
- + break;
- +
- + case OID_GEN_MULTICAST_FRAMES_RCV:
- + DBG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].stats) {
- + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
- + .stats->multicast);
- + retval = 0;
- + }
- + break;
- +
- + case OID_GEN_BROADCAST_BYTES_RCV:
- + DBG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].stats) {
- + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
- + .stats->rx_packets/42*255);
- + retval = 0;
- + }
- + break;
- +
- + case OID_GEN_BROADCAST_FRAMES_RCV:
- + DBG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].stats) {
- + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
- + .stats->rx_packets/42);
- + retval = 0;
- + }
- + break;
- +
- + case OID_GEN_RCV_CRC_ERROR:
- + DBG("%s: OID_GEN_RCV_CRC_ERROR\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].stats) {
- + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
- + .stats->rx_crc_errors);
- + retval = 0;
- + }
- + break;
- +
- + case OID_GEN_TRANSMIT_QUEUE_LENGTH:
- + DBG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __FUNCTION__);
- + *outbuf = __constant_cpu_to_le32 (0);
- + retval = 0;
- + break;
- +#endif /* RNDIS_OPTIONAL_STATS */
- +
- + /* ieee802.3 OIDs (table 4-3) */
- +
- + /* mandatory */
- + case OID_802_3_PERMANENT_ADDRESS:
- + DBG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].dev) {
- + length = ETH_ALEN;
- + memcpy (outbuf,
- + rndis_per_dev_params [configNr].host_mac,
- + length);
- + retval = 0;
- + }
- + break;
- +
- + /* mandatory */
- + case OID_802_3_CURRENT_ADDRESS:
- + DBG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].dev) {
- + length = ETH_ALEN;
- + memcpy (outbuf,
- + rndis_per_dev_params [configNr].host_mac,
- + length);
- + retval = 0;
- + }
- + break;
- +
- + /* mandatory */
- + case OID_802_3_MULTICAST_LIST:
- + DBG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__);
- + /* Multicast base address only */
- + *outbuf = __constant_cpu_to_le32 (0xE0000000);
- + retval = 0;
- + break;
- +
- + /* mandatory */
- + case OID_802_3_MAXIMUM_LIST_SIZE:
- + DBG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __FUNCTION__);
- + /* Multicast base address only */
- + *outbuf = __constant_cpu_to_le32 (1);
- + retval = 0;
- + break;
- +
- + case OID_802_3_MAC_OPTIONS:
- + DBG("%s: OID_802_3_MAC_OPTIONS\n", __FUNCTION__);
- + break;
- +
- + /* ieee802.3 statistics OIDs (table 4-4) */
- +
- + /* mandatory */
- + case OID_802_3_RCV_ERROR_ALIGNMENT:
- + DBG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __FUNCTION__);
- + if (rndis_per_dev_params [configNr].stats) {
- + *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
- + .stats->rx_frame_errors);
- + retval = 0;
- + }
- + break;
- +
- + /* mandatory */
- + case OID_802_3_XMIT_ONE_COLLISION:
- + DBG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __FUNCTION__);
- + *outbuf = __constant_cpu_to_le32 (0);
- + retval = 0;
- + break;
- +
- + /* mandatory */
- + case OID_802_3_XMIT_MORE_COLLISIONS:
- + DBG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __FUNCTION__);
- + *outbuf = __constant_cpu_to_le32 (0);
- + retval = 0;
- + break;
- +
- +#ifdef RNDIS_OPTIONAL_STATS
- + case OID_802_3_XMIT_DEFERRED:
- + DBG("%s: OID_802_3_XMIT_DEFERRED\n", __FUNCTION__);
- + /* TODO */
- + break;
- +
- + case OID_802_3_XMIT_MAX_COLLISIONS:
- + DBG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __FUNCTION__);
- + /* TODO */
- + break;
- +
- + case OID_802_3_RCV_OVERRUN:
- + DBG("%s: OID_802_3_RCV_OVERRUN\n", __FUNCTION__);
- + /* TODO */
- + break;
- +
- + case OID_802_3_XMIT_UNDERRUN:
- + DBG("%s: OID_802_3_XMIT_UNDERRUN\n", __FUNCTION__);
- + /* TODO */
- + break;
- +
- + case OID_802_3_XMIT_HEARTBEAT_FAILURE:
- + DBG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __FUNCTION__);
- + /* TODO */
- + break;
- +
- + case OID_802_3_XMIT_TIMES_CRS_LOST:
- + DBG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __FUNCTION__);
- + /* TODO */
- + break;
- +
- + case OID_802_3_XMIT_LATE_COLLISIONS:
- + DBG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __FUNCTION__);
- + /* TODO */
- + break;
- +#endif /* RNDIS_OPTIONAL_STATS */
- +
- +#ifdef RNDIS_PM
- + /* power management OIDs (table 4-5) */
- + case OID_PNP_CAPABILITIES:
- + DBG("%s: OID_PNP_CAPABILITIES\n", __FUNCTION__);
- +
- + /* for now, no wakeup capabilities */
- + length = sizeof (struct NDIS_PNP_CAPABILITIES);
- + memset(outbuf, 0, length);
- + retval = 0;
- + break;
- + case OID_PNP_QUERY_POWER:
- + DBG("%s: OID_PNP_QUERY_POWER D%d\n", __FUNCTION__,
- + le32_to_cpu(get_unaligned((__le32 *)buf)) - 1);
- + /* only suspend is a real power state, and
- + * it can't be entered by OID_PNP_SET_POWER...
- + */
- + length = 0;
- + retval = 0;
- + break;
- +#endif
- +
- + default:
- + pr_warning("%s: query unknown OID 0x%08X\n",
- + __FUNCTION__, OID);
- + }
- + if (retval < 0)
- + length = 0;
- +
- + resp->InformationBufferLength = cpu_to_le32 (length);
- + r->length = length + sizeof *resp;
- + resp->MessageLength = cpu_to_le32 (r->length);
- + return retval;
- +}
- +
- +static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len,
- + rndis_resp_t *r)
- +{
- + rndis_set_cmplt_type *resp;
- + int i, retval = -ENOTSUPP;
- + struct rndis_params *params;
- +
- + if (!r)
- + return -ENOMEM;
- + resp = (rndis_set_cmplt_type *) r->buf;
- + if (!resp)
- + return -ENOMEM;
- +
- + if (buf_len && rndis_debug > 1) {
- + DBG("set OID %08x value, len %d:\n", OID, buf_len);
- + for (i = 0; i < buf_len; i += 16) {
- + DBG("%03d: %08x %08x %08x %08x\n", i,
- + le32_to_cpu(get_unaligned((__le32 *)
- + &buf[i])),
- + le32_to_cpu(get_unaligned((__le32 *)
- + &buf[i + 4])),
- + le32_to_cpu(get_unaligned((__le32 *)
- + &buf[i + 8])),
- + le32_to_cpu(get_unaligned((__le32 *)
- + &buf[i + 12])));
- + }
- + }
- +
- + params = &rndis_per_dev_params [configNr];
- + switch (OID) {
- + case OID_GEN_CURRENT_PACKET_FILTER:
- +
- + /* these NDIS_PACKET_TYPE_* bitflags are shared with
- + * cdc_filter; it's not RNDIS-specific
- + * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in:
- + * PROMISCUOUS, DIRECTED,
- + * MULTICAST, ALL_MULTICAST, BROADCAST
- + */
- + *params->filter = (u16) le32_to_cpu(get_unaligned(
- + (__le32 *)buf));
- + DBG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n",
- + __FUNCTION__, *params->filter);
- +
- + /* this call has a significant side effect: it's
- + * what makes the packet flow start and stop, like
- + * activating the CDC Ethernet altsetting.
- + */
- +#ifdef RNDIS_PM
- +update_linkstate:
- +#endif
- + retval = 0;
- + if (*params->filter) {
- + params->state = RNDIS_DATA_INITIALIZED;
- + netif_carrier_on(params->dev);
- + if (netif_running(params->dev))
- + netif_wake_queue (params->dev);
- + } else {
- + params->state = RNDIS_INITIALIZED;
- + netif_carrier_off (params->dev);
- + netif_stop_queue (params->dev);
- + }
- + break;
- +
- + case OID_802_3_MULTICAST_LIST:
- + /* I think we can ignore this */
- + DBG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__);
- + retval = 0;
- + break;
- +#if 0
- + case OID_GEN_RNDIS_CONFIG_PARAMETER:
- + {
- + struct rndis_config_parameter *param;
- + param = (struct rndis_config_parameter *) buf;
- + DBG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n",
- + __FUNCTION__,
- + min(cpu_to_le32(param->ParameterNameLength),80),
- + buf + param->ParameterNameOffset);
- + retval = 0;
- + }
- + break;
- +#endif
- +
- +#ifdef RNDIS_PM
- + case OID_PNP_SET_POWER:
- + /* The only real power state is USB suspend, and RNDIS requests
- + * can't enter it; this one isn't really about power. After
- + * resuming, Windows forces a reset, and then SET_POWER D0.
- + * FIXME ... then things go batty; Windows wedges itself.
- + */
- + i = le32_to_cpu(get_unaligned((__le32 *)buf));
- + DBG("%s: OID_PNP_SET_POWER D%d\n", __FUNCTION__, i - 1);
- + switch (i) {
- + case NdisDeviceStateD0:
- + *params->filter = params->saved_filter;
- + goto update_linkstate;
- + case NdisDeviceStateD3:
- + case NdisDeviceStateD2:
- + case NdisDeviceStateD1:
- + params->saved_filter = *params->filter;
- + retval = 0;
- + break;
- + }
- + break;
- +
- +#ifdef RNDIS_WAKEUP
- + // no wakeup support advertised, so wakeup OIDs always fail:
- + // - OID_PNP_ENABLE_WAKE_UP
- + // - OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN
- +#endif
- +
- +#endif /* RNDIS_PM */
- +
- + default:
- + pr_warning("%s: set unknown OID 0x%08X, size %d\n",
- + __FUNCTION__, OID, buf_len);
- + }
- +
- + return retval;
- +}
- +
- +/*
- + * Response Functions
- + */
- +
- +static int rndis_init_response (int configNr, rndis_init_msg_type *buf)
- +{
- + rndis_init_cmplt_type *resp;
- + rndis_resp_t *r;
- +
- + if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP;
- +
- + r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type));
- + if (!r)
- + return -ENOMEM;
- + resp = (rndis_init_cmplt_type *) r->buf;
- +
- + resp->MessageType = __constant_cpu_to_le32 (
- + REMOTE_NDIS_INITIALIZE_CMPLT);
- + resp->MessageLength = __constant_cpu_to_le32 (52);
- + resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
- + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
- + resp->MajorVersion = __constant_cpu_to_le32 (RNDIS_MAJOR_VERSION);
- + resp->MinorVersion = __constant_cpu_to_le32 (RNDIS_MINOR_VERSION);
- + resp->DeviceFlags = __constant_cpu_to_le32 (RNDIS_DF_CONNECTIONLESS);
- + resp->Medium = __constant_cpu_to_le32 (RNDIS_MEDIUM_802_3);
- + resp->MaxPacketsPerTransfer = __constant_cpu_to_le32 (1);
- + resp->MaxTransferSize = cpu_to_le32 (
- + rndis_per_dev_params [configNr].dev->mtu
- + + sizeof (struct ethhdr)
- + + sizeof (struct rndis_packet_msg_type)
- + + 22);
- + resp->PacketAlignmentFactor = __constant_cpu_to_le32 (0);
- + resp->AFListOffset = __constant_cpu_to_le32 (0);
- + resp->AFListSize = __constant_cpu_to_le32 (0);
- +
- + if (rndis_per_dev_params [configNr].ack)
- + rndis_per_dev_params [configNr].ack (
- + rndis_per_dev_params [configNr].dev);
- +
- + return 0;
- +}
- +
- +static int rndis_query_response (int configNr, rndis_query_msg_type *buf)
- +{
- + rndis_query_cmplt_type *resp;
- + rndis_resp_t *r;
- +
- + // DBG("%s: OID = %08X\n", __FUNCTION__, cpu_to_le32(buf->OID));
- + if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP;
- +
- + /*
- + * we need more memory:
- + * gen_ndis_query_resp expects enough space for
- + * rndis_query_cmplt_type followed by data.
- + * oid_supported_list is the largest data reply
- + */
- + r = rndis_add_response (configNr,
- + sizeof (oid_supported_list) + sizeof(rndis_query_cmplt_type));
- + if (!r)
- + return -ENOMEM;
- + resp = (rndis_query_cmplt_type *) r->buf;
- +
- + resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_QUERY_CMPLT);
- + resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
- +
- + if (gen_ndis_query_resp (configNr, le32_to_cpu (buf->OID),
- + le32_to_cpu(buf->InformationBufferOffset)
- + + 8 + (u8 *) buf,
- + le32_to_cpu(buf->InformationBufferLength),
- + r)) {
- + /* OID not supported */
- + resp->Status = __constant_cpu_to_le32 (
- + RNDIS_STATUS_NOT_SUPPORTED);
- + resp->MessageLength = __constant_cpu_to_le32 (sizeof *resp);
- + resp->InformationBufferLength = __constant_cpu_to_le32 (0);
- + resp->InformationBufferOffset = __constant_cpu_to_le32 (0);
- + } else
- + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
- +
- + if (rndis_per_dev_params [configNr].ack)
- + rndis_per_dev_params [configNr].ack (
- + rndis_per_dev_params [configNr].dev);
- + return 0;
- +}
- +
- +static int rndis_set_response (int configNr, rndis_set_msg_type *buf)
- +{
- + u32 BufLength, BufOffset;
- + rndis_set_cmplt_type *resp;
- + rndis_resp_t *r;
- +
- + r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type));
- + if (!r)
- + return -ENOMEM;
- + resp = (rndis_set_cmplt_type *) r->buf;
- +
- + BufLength = le32_to_cpu (buf->InformationBufferLength);
- + BufOffset = le32_to_cpu (buf->InformationBufferOffset);
- +
- +#ifdef VERBOSE
- + DBG("%s: Length: %d\n", __FUNCTION__, BufLength);
- + DBG("%s: Offset: %d\n", __FUNCTION__, BufOffset);
- + DBG("%s: InfoBuffer: ", __FUNCTION__);
- +
- + for (i = 0; i < BufLength; i++) {
- + DBG("%02x ", *(((u8 *) buf) + i + 8 + BufOffset));
- + }
- +
- + DBG("\n");
- +#endif
- +
- + resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_SET_CMPLT);
- + resp->MessageLength = __constant_cpu_to_le32 (16);
- + resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
- + if (gen_ndis_set_resp (configNr, le32_to_cpu (buf->OID),
- + ((u8 *) buf) + 8 + BufOffset, BufLength, r))
- + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_NOT_SUPPORTED);
- + else
- + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
- +
- + if (rndis_per_dev_params [configNr].ack)
- + rndis_per_dev_params [configNr].ack (
- + rndis_per_dev_params [configNr].dev);
- +
- + return 0;
- +}
- +
- +static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf)
- +{
- + rndis_reset_cmplt_type *resp;
- + rndis_resp_t *r;
- +
- + r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type));
- + if (!r)
- + return -ENOMEM;
- + resp = (rndis_reset_cmplt_type *) r->buf;
- +
- + resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_RESET_CMPLT);
- + resp->MessageLength = __constant_cpu_to_le32 (16);
- + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
- + /* resent information */
- + resp->AddressingReset = __constant_cpu_to_le32 (1);
- +
- + if (rndis_per_dev_params [configNr].ack)
- + rndis_per_dev_params [configNr].ack (
- + rndis_per_dev_params [configNr].dev);
- +
- + return 0;
- +}
- +
- +static int rndis_keepalive_response (int configNr,
- + rndis_keepalive_msg_type *buf)
- +{
- + rndis_keepalive_cmplt_type *resp;
- + rndis_resp_t *r;
- +
- + /* host "should" check only in RNDIS_DATA_INITIALIZED state */
- +
- + r = rndis_add_response (configNr, sizeof (rndis_keepalive_cmplt_type));
- + if (!r)
- + return -ENOMEM;
- + resp = (rndis_keepalive_cmplt_type *) r->buf;
- +
- + resp->MessageType = __constant_cpu_to_le32 (
- + REMOTE_NDIS_KEEPALIVE_CMPLT);
- + resp->MessageLength = __constant_cpu_to_le32 (16);
- + resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
- + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
- +
- + if (rndis_per_dev_params [configNr].ack)
- + rndis_per_dev_params [configNr].ack (
- + rndis_per_dev_params [configNr].dev);
- +
- + return 0;
- +}
- +
- +
- +/*
- + * Device to Host Comunication
- + */
- +static int rndis_indicate_status_msg (int configNr, u32 status)
- +{
- + rndis_indicate_status_msg_type *resp;
- + rndis_resp_t *r;
- +
- + if (rndis_per_dev_params [configNr].state == RNDIS_UNINITIALIZED)
- + return -ENOTSUPP;
- +
- + r = rndis_add_response (configNr,
- + sizeof (rndis_indicate_status_msg_type));
- + if (!r)
- + return -ENOMEM;
- + resp = (rndis_indicate_status_msg_type *) r->buf;
- +
- + resp->MessageType = __constant_cpu_to_le32 (
- + REMOTE_NDIS_INDICATE_STATUS_MSG);
- + resp->MessageLength = __constant_cpu_to_le32 (20);
- + resp->Status = cpu_to_le32 (status);
- + resp->StatusBufferLength = __constant_cpu_to_le32 (0);
- + resp->StatusBufferOffset = __constant_cpu_to_le32 (0);
- +
- + if (rndis_per_dev_params [configNr].ack)
- + rndis_per_dev_params [configNr].ack (
- + rndis_per_dev_params [configNr].dev);
- + return 0;
- +}
- +
- +int rndis_signal_connect (int configNr)
- +{
- + rndis_per_dev_params [configNr].media_state
- + = NDIS_MEDIA_STATE_CONNECTED;
- + return rndis_indicate_status_msg (configNr,
- + RNDIS_STATUS_MEDIA_CONNECT);
- +}
- +
- +int rndis_signal_disconnect (int configNr)
- +{
- + rndis_per_dev_params [configNr].media_state
- + = NDIS_MEDIA_STATE_DISCONNECTED;
- + return rndis_indicate_status_msg (configNr,
- + RNDIS_STATUS_MEDIA_DISCONNECT);
- +}
- +
- +void rndis_uninit (int configNr)
- +{
- + u8 *buf;
- + u32 length;
- +
- + if (configNr >= RNDIS_MAX_CONFIGS)
- + return;
- + rndis_per_dev_params [configNr].used = 0;
- + rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED;
- +
- + /* drain the response queue */
- + while ((buf = rndis_get_next_response(configNr, &length)))
- + rndis_free_response(configNr, buf);
- +}
- +
- +void rndis_set_host_mac (int configNr, const u8 *addr)
- +{
- + rndis_per_dev_params [configNr].host_mac = addr;
- +}
- +
- +/*
- + * Message Parser
- + */
- +int rndis_msg_parser (u8 configNr, u8 *buf)
- +{
- + u32 MsgType, MsgLength;
- + __le32 *tmp;
- + struct rndis_params *params;
- +
- + if (!buf)
- + return -ENOMEM;
- +
- + tmp = (__le32 *) buf;
- + MsgType = le32_to_cpu(get_unaligned(tmp++));
- + MsgLength = le32_to_cpu(get_unaligned(tmp++));
- +
- + if (configNr >= RNDIS_MAX_CONFIGS)
- + return -ENOTSUPP;
- + params = &rndis_per_dev_params [configNr];
- +
- + /* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for
- + * rx/tx statistics and link status, in addition to KEEPALIVE traffic
- + * and normal HC level polling to see if there's any IN traffic.
- + */
- +
- + /* For USB: responses may take up to 10 seconds */
- + switch (MsgType) {
- + case REMOTE_NDIS_INITIALIZE_MSG:
- + DBG("%s: REMOTE_NDIS_INITIALIZE_MSG\n",
- + __FUNCTION__ );
- + params->state = RNDIS_INITIALIZED;
- + return rndis_init_response (configNr,
- + (rndis_init_msg_type *) buf);
- +
- + case REMOTE_NDIS_HALT_MSG:
- + DBG("%s: REMOTE_NDIS_HALT_MSG\n",
- + __FUNCTION__ );
- + params->state = RNDIS_UNINITIALIZED;
- + if (params->dev) {
- + netif_carrier_off (params->dev);
- + netif_stop_queue (params->dev);
- + }
- + return 0;
- +
- + case REMOTE_NDIS_QUERY_MSG:
- + return rndis_query_response (configNr,
- + (rndis_query_msg_type *) buf);
- +
- + case REMOTE_NDIS_SET_MSG:
- + return rndis_set_response (configNr,
- + (rndis_set_msg_type *) buf);
- +
- + case REMOTE_NDIS_RESET_MSG:
- + DBG("%s: REMOTE_NDIS_RESET_MSG\n",
- + __FUNCTION__ );
- + return rndis_reset_response (configNr,
- + (rndis_reset_msg_type *) buf);
- +
- + case REMOTE_NDIS_KEEPALIVE_MSG:
- + /* For USB: host does this every 5 seconds */
- + if (rndis_debug > 1)
- + DBG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n",
- + __FUNCTION__ );
- + return rndis_keepalive_response (configNr,
- + (rndis_keepalive_msg_type *)
- + buf);
- +
- + default:
- + /* At least Windows XP emits some undefined RNDIS messages.
- + * In one case those messages seemed to relate to the host
- + * suspending itself.
- + */
- + pr_warning("%s: unknown RNDIS message 0x%08X len %d\n",
- + __FUNCTION__ , MsgType, MsgLength);
- + {
- + unsigned i;
- + for (i = 0; i < MsgLength; i += 16) {
- + DBG("%03d: "
- + " %02x %02x %02x %02x"
- + " %02x %02x %02x %02x"
- + " %02x %02x %02x %02x"
- + " %02x %02x %02x %02x"
- + "\n",
- + i,
- + buf[i], buf [i+1],
- + buf[i+2], buf[i+3],
- + buf[i+4], buf [i+5],
- + buf[i+6], buf[i+7],
- + buf[i+8], buf [i+9],
- + buf[i+10], buf[i+11],
- + buf[i+12], buf [i+13],
- + buf[i+14], buf[i+15]);
- + }
- + }
- + break;
- + }
- +
- + return -ENOTSUPP;
- +}
- +
- +int rndis_register (int (* rndis_control_ack) (struct net_device *))
- +{
- + u8 i;
- +
- + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
- + if (!rndis_per_dev_params [i].used) {
- + rndis_per_dev_params [i].used = 1;
- + rndis_per_dev_params [i].ack = rndis_control_ack;
- + DBG("%s: configNr = %d\n", __FUNCTION__, i);
- + return i;
- + }
- + }
- + DBG("failed\n");
- +
- + return -1;
- +}
- +
- +void rndis_deregister (int configNr)
- +{
- + DBG("%s: \n", __FUNCTION__ );
- +
- + if (configNr >= RNDIS_MAX_CONFIGS) return;
- + rndis_per_dev_params [configNr].used = 0;
- +
- + return;
- +}
- +
- +int rndis_set_param_dev (u8 configNr, struct net_device *dev,
- + struct net_device_stats *stats,
- + u16 *cdc_filter)
- +{
- + DBG("%s:\n", __FUNCTION__ );
- + if (!dev || !stats) return -1;
- + if (configNr >= RNDIS_MAX_CONFIGS) return -1;
- +
- + rndis_per_dev_params [configNr].dev = dev;
- + rndis_per_dev_params [configNr].stats = stats;
- + rndis_per_dev_params [configNr].filter = cdc_filter;
- +
- + return 0;
- +}
- +
- +int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr)
- +{
- + DBG("%s:\n", __FUNCTION__ );
- + if (!vendorDescr) return -1;
- + if (configNr >= RNDIS_MAX_CONFIGS) return -1;
- +
- + rndis_per_dev_params [configNr].vendorID = vendorID;
- + rndis_per_dev_params [configNr].vendorDescr = vendorDescr;
- +
- + return 0;
- +}
- +
- +int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed)
- +{
- + DBG("%s: %u %u\n", __FUNCTION__, medium, speed);
- + if (configNr >= RNDIS_MAX_CONFIGS) return -1;
- +
- + rndis_per_dev_params [configNr].medium = medium;
- + rndis_per_dev_params [configNr].speed = speed;
- +
- + return 0;
- +}
- +
- +void rndis_add_hdr (struct sk_buff *skb)
- +{
- + struct rndis_packet_msg_type *header;
- +
- + if (!skb)
- + return;
- + header = (void *) skb_push (skb, sizeof *header);
- + memset (header, 0, sizeof *header);
- + header->MessageType = __constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG);
- + header->MessageLength = cpu_to_le32(skb->len);
- + header->DataOffset = __constant_cpu_to_le32 (36);
- + header->DataLength = cpu_to_le32(skb->len - sizeof *header);
- +}
- +
- +void rndis_free_response (int configNr, u8 *buf)
- +{
- + rndis_resp_t *r;
- + struct list_head *act, *tmp;
- +
- + list_for_each_safe (act, tmp,
- + &(rndis_per_dev_params [configNr].resp_queue))
- + {
- + r = list_entry (act, rndis_resp_t, list);
- + if (r && r->buf == buf) {
- + list_del (&r->list);
- + kfree (r);
- + }
- + }
- +}
- +
- +u8 *rndis_get_next_response (int configNr, u32 *length)
- +{
- + rndis_resp_t *r;
- + struct list_head *act, *tmp;
- +
- + if (!length) return NULL;
- +
- + list_for_each_safe (act, tmp,
- + &(rndis_per_dev_params [configNr].resp_queue))
- + {
- + r = list_entry (act, rndis_resp_t, list);
- + if (!r->send) {
- + r->send = 1;
- + *length = r->length;
- + return r->buf;
- + }
- + }
- +
- + return NULL;
- +}
- +
- +static rndis_resp_t *rndis_add_response (int configNr, u32 length)
- +{
- + rndis_resp_t *r;
- +
- + /* NOTE: this gets copied into ether.c USB_BUFSIZ bytes ... */
- + r = kmalloc (sizeof (rndis_resp_t) + length, GFP_ATOMIC);
- + if (!r) return NULL;
- +
- + r->buf = (u8 *) (r + 1);
- + r->length = length;
- + r->send = 0;
- +
- + list_add_tail (&r->list,
- + &(rndis_per_dev_params [configNr].resp_queue));
- + return r;
- +}
- +
- +int rndis_rm_hdr(struct sk_buff *skb)
- +{
- + /* tmp points to a struct rndis_packet_msg_type */
- + __le32 *tmp = (void *) skb->data;
- +
- + /* MessageType, MessageLength */
- + if (__constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG)
- + != get_unaligned(tmp++))
- + return -EINVAL;
- + tmp++;
- +
- + /* DataOffset, DataLength */
- + if (!skb_pull(skb, le32_to_cpu(get_unaligned(tmp++))
- + + 8 /* offset of DataOffset */))
- + return -EOVERFLOW;
- + skb_trim(skb, le32_to_cpu(get_unaligned(tmp++)));
- +
- + return 0;
- +}
- +
- +#ifdef CONFIG_USB_GADGET_DEBUG_FILES
- +
- +static int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof,
- + void *data)
- +{
- + char *out = page;
- + int len;
- + rndis_params *param = (rndis_params *) data;
- +
- + out += snprintf (out, count,
- + "Config Nr. %d\n"
- + "used : %s\n"
- + "state : %s\n"
- + "medium : 0x%08X\n"
- + "speed : %d\n"
- + "cable : %s\n"
- + "vendor ID : 0x%08X\n"
- + "vendor : %s\n",
- + param->confignr, (param->used) ? "y" : "n",
- + ({ char *s = "?";
- + switch (param->state) {
- + case RNDIS_UNINITIALIZED:
- + s = "RNDIS_UNINITIALIZED"; break;
- + case RNDIS_INITIALIZED:
- + s = "RNDIS_INITIALIZED"; break;
- + case RNDIS_DATA_INITIALIZED:
- + s = "RNDIS_DATA_INITIALIZED"; break;
- + }; s; }),
- + param->medium,
- + (param->media_state) ? 0 : param->speed*100,
- + (param->media_state) ? "disconnected" : "connected",
- + param->vendorID, param->vendorDescr);
- +
- + len = out - page;
- + len -= off;
- +
- + if (len < count) {
- + *eof = 1;
- + if (len <= 0)
- + return 0;
- + } else
- + len = count;
- +
- + *start = page + off;
- + return len;
- +}
- +
- +static int rndis_proc_write (struct file *file, const char __user *buffer,
- + unsigned long count, void *data)
- +{
- + rndis_params *p = data;
- + u32 speed = 0;
- + int i, fl_speed = 0;
- +
- + for (i = 0; i < count; i++) {
- + char c;
- + if (get_user(c, buffer))
- + return -EFAULT;
- + switch (c) {
- + case '0':
- + case '1':
- + case '2':
- + case '3':
- + case '4':
- + case '5':
- + case '6':
- + case '7':
- + case '8':
- + case '9':
- + fl_speed = 1;
- + speed = speed*10 + c - '0';
- + break;
- + case 'C':
- + case 'c':
- + rndis_signal_connect (p->confignr);
- + break;
- + case 'D':
- + case 'd':
- + rndis_signal_disconnect(p->confignr);
- + break;
- + default:
- + if (fl_speed) p->speed = speed;
- + else DBG("%c is not valid\n", c);
- + break;
- + }
- +
- + buffer++;
- + }
- +
- + return count;
- +}
- +
- +#define NAME_TEMPLATE "driver/rndis-%03d"
- +
- +static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];
- +
- +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
- +
- +
- +int __devinit rndis_init (void)
- +{
- + u8 i;
- +
- + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
- +#ifdef CONFIG_USB_GADGET_DEBUG_FILES
- + char name [20];
- +
- + sprintf (name, NAME_TEMPLATE, i);
- + if (!(rndis_connect_state [i]
- + = create_proc_entry (name, 0660, NULL)))
- + {
- + DBG("%s :remove entries", __FUNCTION__);
- + while (i) {
- + sprintf (name, NAME_TEMPLATE, --i);
- + remove_proc_entry (name, NULL);
- + }
- + DBG("\n");
- + return -EIO;
- + }
- +
- + rndis_connect_state [i]->write_proc = rndis_proc_write;
- + rndis_connect_state [i]->read_proc = rndis_proc_read;
- + rndis_connect_state [i]->data = (void *)
- + (rndis_per_dev_params + i);
- +#endif
- + rndis_per_dev_params [i].confignr = i;
- + rndis_per_dev_params [i].used = 0;
- + rndis_per_dev_params [i].state = RNDIS_UNINITIALIZED;
- + rndis_per_dev_params [i].media_state
- + = NDIS_MEDIA_STATE_DISCONNECTED;
- + INIT_LIST_HEAD (&(rndis_per_dev_params [i].resp_queue));
- + }
- +
- + return 0;
- +}
- +
- +void rndis_exit (void)
- +{
- +#ifdef CONFIG_USB_GADGET_DEBUG_FILES
- + u8 i;
- + char name [20];
- +
- + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
- + sprintf (name, NAME_TEMPLATE, i);
- + remove_proc_entry (name, NULL);
- + }
- +#endif
- +}
- +
- diff -Naurp a/drivers/usb/function/rndis.h b/drivers/usb/function/rndis.h
- --- a/drivers/usb/function/rndis.h 1970-01-01 01:00:00.000000000 +0100
- +++ b/drivers/usb/function/rndis.h 2009-12-18 23:16:54.619476954 +0100
- @@ -0,0 +1,270 @@
- +/*
- + * RNDIS Definitions for Remote NDIS
- + *
- + * Version: $Id: rndis.h,v 1.15 2004/03/25 21:33:46 robert Exp $
- + *
- + * Authors: Benedikt Spranger, Pengutronix
- + * Robert Schwebel, Pengutronix
- + *
- + * This program is free software; you can redistribute it and/or
- + * modify it under the terms of the GNU General Public License
- + * version 2, as published by the Free Software Foundation.
- + *
- + * This software was originally developed in conformance with
- + * Microsoft's Remote NDIS Specification License Agreement.
- + */
- +
- +#ifndef _LINUX_RNDIS_H
- +#define _LINUX_RNDIS_H
- +
- +#include "ndis.h"
- +
- +#define RNDIS_MAXIMUM_FRAME_SIZE 1518
- +#define RNDIS_MAX_TOTAL_SIZE 1558
- +
- +/* Remote NDIS Versions */
- +#define RNDIS_MAJOR_VERSION 1
- +#define RNDIS_MINOR_VERSION 0
- +
- +/* Status Values */
- +#define RNDIS_STATUS_SUCCESS 0x00000000U /* Success */
- +#define RNDIS_STATUS_FAILURE 0xC0000001U /* Unspecified error */
- +#define RNDIS_STATUS_INVALID_DATA 0xC0010015U /* Invalid data */
- +#define RNDIS_STATUS_NOT_SUPPORTED 0xC00000BBU /* Unsupported request */
- +#define RNDIS_STATUS_MEDIA_CONNECT 0x4001000BU /* Device connected */
- +#define RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000CU /* Device disconnected */
- +/* For all not specified status messages:
- + * RNDIS_STATUS_Xxx -> NDIS_STATUS_Xxx
- + */
- +
- +/* Message Set for Connectionless (802.3) Devices */
- +#define REMOTE_NDIS_PACKET_MSG 0x00000001U
- +#define REMOTE_NDIS_INITIALIZE_MSG 0x00000002U /* Initialize device */
- +#define REMOTE_NDIS_HALT_MSG 0x00000003U
- +#define REMOTE_NDIS_QUERY_MSG 0x00000004U
- +#define REMOTE_NDIS_SET_MSG 0x00000005U
- +#define REMOTE_NDIS_RESET_MSG 0x00000006U
- +#define REMOTE_NDIS_INDICATE_STATUS_MSG 0x00000007U
- +#define REMOTE_NDIS_KEEPALIVE_MSG 0x00000008U
- +
- +/* Message completion */
- +#define REMOTE_NDIS_INITIALIZE_CMPLT 0x80000002U
- +#define REMOTE_NDIS_QUERY_CMPLT 0x80000004U
- +#define REMOTE_NDIS_SET_CMPLT 0x80000005U
- +#define REMOTE_NDIS_RESET_CMPLT 0x80000006U
- +#define REMOTE_NDIS_KEEPALIVE_CMPLT 0x80000008U
- +
- +/* Device Flags */
- +#define RNDIS_DF_CONNECTIONLESS 0x00000001U
- +#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002U
- +
- +#define RNDIS_MEDIUM_802_3 0x00000000U
- +
- +/* from drivers/net/sk98lin/h/skgepnmi.h */
- +#define OID_PNP_CAPABILITIES 0xFD010100
- +#define OID_PNP_SET_POWER 0xFD010101
- +#define OID_PNP_QUERY_POWER 0xFD010102
- +#define OID_PNP_ADD_WAKE_UP_PATTERN 0xFD010103
- +#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xFD010104
- +#define OID_PNP_ENABLE_WAKE_UP 0xFD010106
- +
- +
- +typedef struct rndis_init_msg_type
- +{
- + __le32 MessageType;
- + __le32 MessageLength;
- + __le32 RequestID;
- + __le32 MajorVersion;
- + __le32 MinorVersion;
- + __le32 MaxTransferSize;
- +} rndis_init_msg_type;
- +
- +typedef struct rndis_init_cmplt_type
- +{
- + __le32 MessageType;
- + __le32 MessageLength;
- + __le32 RequestID;
- + __le32 Status;
- + __le32 MajorVersion;
- + __le32 MinorVersion;
- + __le32 DeviceFlags;
- + __le32 Medium;
- + __le32 MaxPacketsPerTransfer;
- + __le32 MaxTransferSize;
- + __le32 PacketAlignmentFactor;
- + __le32 AFListOffset;
- + __le32 AFListSize;
- +} rndis_init_cmplt_type;
- +
- +typedef struct rndis_halt_msg_type
- +{
- + __le32 MessageType;
- + __le32 MessageLength;
- + __le32 RequestID;
- +} rndis_halt_msg_type;
- +
- +typedef struct rndis_query_msg_type
- +{
- + __le32 MessageType;
- + __le32 MessageLength;
- + __le32 RequestID;
- + __le32 OID;
- + __le32 InformationBufferLength;
- + __le32 InformationBufferOffset;
- + __le32 DeviceVcHandle;
- +} rndis_query_msg_type;
- +
- +typedef struct rndis_query_cmplt_type
- +{
- + __le32 MessageType;
- + __le32 MessageLength;
- + __le32 RequestID;
- + __le32 Status;
- + __le32 InformationBufferLength;
- + __le32 InformationBufferOffset;
- +} rndis_query_cmplt_type;
- +
- +typedef struct rndis_set_msg_type
- +{
- + __le32 MessageType;
- + __le32 MessageLength;
- + __le32 RequestID;
- + __le32 OID;
- + __le32 InformationBufferLength;
- + __le32 InformationBufferOffset;
- + __le32 DeviceVcHandle;
- +} rndis_set_msg_type;
- +
- +typedef struct rndis_set_cmplt_type
- +{
- + __le32 MessageType;
- + __le32 MessageLength;
- + __le32 RequestID;
- + __le32 Status;
- +} rndis_set_cmplt_type;
- +
- +typedef struct rndis_reset_msg_type
- +{
- + __le32 MessageType;
- + __le32 MessageLength;
- + __le32 Reserved;
- +} rndis_reset_msg_type;
- +
- +typedef struct rndis_reset_cmplt_type
- +{
- + __le32 MessageType;
- + __le32 MessageLength;
- + __le32 Status;
- + __le32 AddressingReset;
- +} rndis_reset_cmplt_type;
- +
- +typedef struct rndis_indicate_status_msg_type
- +{
- + __le32 MessageType;
- + __le32 MessageLength;
- + __le32 Status;
- + __le32 StatusBufferLength;
- + __le32 StatusBufferOffset;
- +} rndis_indicate_status_msg_type;
- +
- +typedef struct rndis_keepalive_msg_type
- +{
- + __le32 MessageType;
- + __le32 MessageLength;
- + __le32 RequestID;
- +} rndis_keepalive_msg_type;
- +
- +typedef struct rndis_keepalive_cmplt_type
- +{
- + __le32 MessageType;
- + __le32 MessageLength;
- + __le32 RequestID;
- + __le32 Status;
- +} rndis_keepalive_cmplt_type;
- +
- +struct rndis_packet_msg_type
- +{
- + __le32 MessageType;
- + __le32 MessageLength;
- + __le32 DataOffset;
- + __le32 DataLength;
- + __le32 OOBDataOffset;
- + __le32 OOBDataLength;
- + __le32 NumOOBDataElements;
- + __le32 PerPacketInfoOffset;
- + __le32 PerPacketInfoLength;
- + __le32 VcHandle;
- + __le32 Reserved;
- +} __attribute__ ((packed));
- +
- +struct rndis_config_parameter
- +{
- + __le32 ParameterNameOffset;
- + __le32 ParameterNameLength;
- + __le32 ParameterType;
- + __le32 ParameterValueOffset;
- + __le32 ParameterValueLength;
- +};
- +
- +/* implementation specific */
- +enum rndis_state
- +{
- + RNDIS_UNINITIALIZED,
- + RNDIS_INITIALIZED,
- + RNDIS_DATA_INITIALIZED,
- +};
- +
- +typedef struct rndis_resp_t
- +{
- + struct list_head list;
- + u8 *buf;
- + u32 length;
- + int send;
- +} rndis_resp_t;
- +
- +typedef struct rndis_params
- +{
- + u8 confignr;
- + u8 used;
- + u16 saved_filter;
- + enum rndis_state state;
- + u32 medium;
- + u32 speed;
- + u32 media_state;
- +
- + const u8 *host_mac;
- + u16 *filter;
- + struct net_device *dev;
- + struct net_device_stats *stats;
- +
- + u32 vendorID;
- + const char *vendorDescr;
- + int (*ack) (struct net_device *);
- + struct list_head resp_queue;
- +} rndis_params;
- +
- +/* RNDIS Message parser and other useless functions */
- +int rndis_msg_parser (u8 configNr, u8 *buf);
- +int rndis_register (int (*rndis_control_ack) (struct net_device *));
- +void rndis_deregister (int configNr);
- +int rndis_set_param_dev (u8 configNr, struct net_device *dev,
- + struct net_device_stats *stats,
- + u16 *cdc_filter);
- +int rndis_set_param_vendor (u8 configNr, u32 vendorID,
- + const char *vendorDescr);
- +int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed);
- +void rndis_add_hdr (struct sk_buff *skb);
- +int rndis_rm_hdr (struct sk_buff *skb);
- +u8 *rndis_get_next_response (int configNr, u32 *length);
- +void rndis_free_response (int configNr, u8 *buf);
- +
- +void rndis_uninit (int configNr);
- +int rndis_signal_connect (int configNr);
- +int rndis_signal_disconnect (int configNr);
- +int rndis_state (int configNr);
- +extern void rndis_set_host_mac (int configNr, const u8 *addr);
- +
- +int __devinit rndis_init (void);
- +void rndis_exit (void);
- +
- +#endif /* _LINUX_RNDIS_H */
- diff -Naurp a/drivers/usb/function/ums.c b/drivers/usb/function/ums.c
- --- a/drivers/usb/function/ums.c 2009-12-18 22:11:24.603494666 +0100
- +++ b/drivers/usb/function/ums.c 2009-12-18 22:11:33.935477884 +0100
- @@ -443,6 +443,8 @@ static struct usb_function usb_func_ums
- .ifc_ept_count = 2,
- .ifc_ept_type = { EPT_BULK_OUT, EPT_BULK_IN },
- +
- + .position_bit = USB_FUNCTION_MASS_STORAGE_NUM,
- };
- static int __init ums_init(void)
- diff -Naurp a/drivers/usb/function/usb_function.h b/drivers/usb/function/usb_function.h
- --- a/drivers/usb/function/usb_function.h 2009-12-18 22:11:51.183482910 +0100
- +++ b/drivers/usb/function/usb_function.h 2009-12-18 22:11:58.127481466 +0100
- @@ -24,6 +24,30 @@
- #define EPT_BULK_IN 1
- #define EPT_BULK_OUT 2
- +#define EPT_INT_IN 3
- +#define EPT_INT_OUT 4
- +
- +#define STRING_UMS 4
- +#define STRING_ADB 5
- +#define STRING_ES 6
- +#define STRING_DIAG 7
- +#define STRING_FSERIAL 8
- +#define STRING_PROJECTOR 9
- +#define STRING_FSYNC 10
- +#define STRING_MTP 11
- +
- +typedef enum {
- + USB_FUNCTION_MASS_STORAGE_NUM = 0, /* default, don't change position */
- + USB_FUNCTION_ADB_NUM,
- + USB_FUNCTION_INTERNET_SHARING_NUM,
- + USB_FUNCTION_DIAG_NUM,
- + USB_FUNCTION_FSERIAL_NUM,
- + USB_FUNCTION_PROJECTOR_NUM,
- + USB_FUNCTION_FSYNC_NUM,
- + USB_FUNCTION_MTP_TUNNEL_NUM,
- + USB_FUNCTION_ZERO_NUM,
- + USB_FUNCTION_LOOPBACK_NUM,
- +} usb_function_position;
- struct usb_endpoint;
- @@ -40,6 +64,12 @@ struct usb_request
- struct list_head list;
- };
- +struct usb_fi_ept
- +{
- + struct usb_endpoint *ept;
- + struct usb_endpoint_descriptor desc;
- +};
- +
- struct usb_function
- {
- /* bind() is called once when the function has had its endpoints
- @@ -83,6 +113,9 @@ struct usb_function
- const char *name;
- void *context;
- + /* number of interfaces */
- + unsigned char ifc_num;
- +
- /* interface class/subclass/protocol for descriptor */
- unsigned char ifc_class;
- unsigned char ifc_subclass;
- @@ -99,12 +132,23 @@ struct usb_function
- ** included in the configuration descriptor
- */
- unsigned char disabled;
- +
- + /* static bit position for product id */
- + usb_function_position position_bit;
- +
- + /* to copy non-standard or multiple interfaces to the descriptor */
- + /* return the number of bytes copied */
- + size_t (*ifc_copy)(char *buf, size_t len, int ifc_number);
- };
- int usb_function_register(struct usb_function *driver);
- void usb_function_enable(const char *function, int enable);
- +int return_usb_function_enabled(const char *function);
- +
- +struct usb_fi_ept *get_ept_info(const char *function);
- +
- /* Allocate a USB request.
- ** Must be called from a context that can sleep.
- ** If bufsize is nonzero, req->buf will be allocated for
- diff -Naurp a/drivers/usb/function/zero.c b/drivers/usb/function/zero.c
- --- a/drivers/usb/function/zero.c 2009-12-18 22:12:13.391498743 +0100
- +++ b/drivers/usb/function/zero.c 2009-12-18 22:12:19.359477295 +0100
- @@ -108,6 +108,8 @@ static struct usb_function usb_func_zero
- .ifc_ept_count = 1,
- .ifc_ept_type = { EPT_BULK_IN },
- +
- + .position_bit = USB_FUNCTION_ZERO_NUM,
- };
- static int __init zero_init(void)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement