linux/drivers/staging/greybus/usb.c
<<
>>
Prefs
   1/*
   2 * USB host driver for the Greybus "generic" USB module.
   3 *
   4 * Copyright 2014 Google Inc.
   5 * Copyright 2014 Linaro Ltd.
   6 *
   7 * Released under the GPLv2 only.
   8 *
   9 */
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/slab.h>
  13#include <linux/usb.h>
  14#include <linux/usb/hcd.h>
  15
  16#include "greybus.h"
  17#include "gbphy.h"
  18
  19/* Greybus USB request types */
  20#define GB_USB_TYPE_HCD_START           0x02
  21#define GB_USB_TYPE_HCD_STOP            0x03
  22#define GB_USB_TYPE_HUB_CONTROL         0x04
  23
  24struct gb_usb_hub_control_request {
  25        __le16 typeReq;
  26        __le16 wValue;
  27        __le16 wIndex;
  28        __le16 wLength;
  29};
  30
  31struct gb_usb_hub_control_response {
  32        u8 buf[0];
  33};
  34
  35struct gb_usb_device {
  36        struct gb_connection *connection;
  37        struct gbphy_device *gbphy_dev;
  38};
  39
  40static inline struct gb_usb_device *to_gb_usb_device(struct usb_hcd *hcd)
  41{
  42        return (struct gb_usb_device *)hcd->hcd_priv;
  43}
  44
  45static inline struct usb_hcd *gb_usb_device_to_hcd(struct gb_usb_device *dev)
  46{
  47        return container_of((void *)dev, struct usb_hcd, hcd_priv);
  48}
  49
  50static void hcd_stop(struct usb_hcd *hcd)
  51{
  52        struct gb_usb_device *dev = to_gb_usb_device(hcd);
  53        int ret;
  54
  55        ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_STOP,
  56                                NULL, 0, NULL, 0);
  57        if (ret)
  58                dev_err(&dev->gbphy_dev->dev, "HCD stop failed '%d'\n", ret);
  59}
  60
  61static int hcd_start(struct usb_hcd *hcd)
  62{
  63        struct usb_bus *bus = hcd_to_bus(hcd);
  64        struct gb_usb_device *dev = to_gb_usb_device(hcd);
  65        int ret;
  66
  67        ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_START,
  68                                NULL, 0, NULL, 0);
  69        if (ret) {
  70                dev_err(&dev->gbphy_dev->dev, "HCD start failed '%d'\n", ret);
  71                return ret;
  72        }
  73
  74        hcd->state = HC_STATE_RUNNING;
  75        if (bus->root_hub)
  76                usb_hcd_resume_root_hub(hcd);
  77        return 0;
  78}
  79
  80static int urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
  81{
  82        return -ENXIO;
  83}
  84
  85static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
  86{
  87        return -ENXIO;
  88}
  89
  90static int get_frame_number(struct usb_hcd *hcd)
  91{
  92        return 0;
  93}
  94
  95static int hub_status_data(struct usb_hcd *hcd, char *buf)
  96{
  97        return 0;
  98}
  99
 100static int hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
 101                       char *buf, u16 wLength)
 102{
 103        struct gb_usb_device *dev = to_gb_usb_device(hcd);
 104        struct gb_operation *operation;
 105        struct gb_usb_hub_control_request *request;
 106        struct gb_usb_hub_control_response *response;
 107        size_t response_size;
 108        int ret;
 109
 110        /* FIXME: handle unspecified lengths */
 111        response_size = sizeof(*response) + wLength;
 112
 113        operation = gb_operation_create(dev->connection,
 114                                        GB_USB_TYPE_HUB_CONTROL,
 115                                        sizeof(*request),
 116                                        response_size,
 117                                        GFP_KERNEL);
 118        if (!operation)
 119                return -ENOMEM;
 120
 121        request = operation->request->payload;
 122        request->typeReq = cpu_to_le16(typeReq);
 123        request->wValue = cpu_to_le16(wValue);
 124        request->wIndex = cpu_to_le16(wIndex);
 125        request->wLength = cpu_to_le16(wLength);
 126
 127        ret = gb_operation_request_send_sync(operation);
 128        if (ret)
 129                goto out;
 130
 131        if (wLength) {
 132                /* Greybus core has verified response size */
 133                response = operation->response->payload;
 134                memcpy(buf, response->buf, wLength);
 135        }
 136out:
 137        gb_operation_put(operation);
 138
 139        return ret;
 140}
 141
 142static struct hc_driver usb_gb_hc_driver = {
 143        .description = "greybus-hcd",
 144        .product_desc = "Greybus USB Host Controller",
 145        .hcd_priv_size = sizeof(struct gb_usb_device),
 146
 147        .flags = HCD_USB2,
 148
 149        .start = hcd_start,
 150        .stop = hcd_stop,
 151
 152        .urb_enqueue = urb_enqueue,
 153        .urb_dequeue = urb_dequeue,
 154
 155        .get_frame_number = get_frame_number,
 156        .hub_status_data = hub_status_data,
 157        .hub_control = hub_control,
 158};
 159
 160static int gb_usb_probe(struct gbphy_device *gbphy_dev,
 161                        const struct gbphy_device_id *id)
 162{
 163        struct gb_connection *connection;
 164        struct device *dev = &gbphy_dev->dev;
 165        struct gb_usb_device *gb_usb_dev;
 166        struct usb_hcd *hcd;
 167        int retval;
 168
 169        hcd = usb_create_hcd(&usb_gb_hc_driver, dev, dev_name(dev));
 170        if (!hcd)
 171                return -ENOMEM;
 172
 173        connection = gb_connection_create(gbphy_dev->bundle,
 174                                          le16_to_cpu(gbphy_dev->cport_desc->id),
 175                                          NULL);
 176        if (IS_ERR(connection)) {
 177                retval = PTR_ERR(connection);
 178                goto exit_usb_put;
 179        }
 180
 181        gb_usb_dev = to_gb_usb_device(hcd);
 182        gb_usb_dev->connection = connection;
 183        gb_connection_set_data(connection, gb_usb_dev);
 184        gb_usb_dev->gbphy_dev = gbphy_dev;
 185        gb_gbphy_set_data(gbphy_dev, gb_usb_dev);
 186
 187        hcd->has_tt = 1;
 188
 189        retval = gb_connection_enable(connection);
 190        if (retval)
 191                goto exit_connection_destroy;
 192
 193        /*
 194         * FIXME: The USB bridged-PHY protocol driver depends on changes to
 195         *        USB core which are not yet upstream.
 196         *
 197         *        Disable for now.
 198         */
 199        if (1) {
 200                dev_warn(dev, "USB protocol disabled\n");
 201                retval = -EPROTONOSUPPORT;
 202                goto exit_connection_disable;
 203        }
 204
 205        retval = usb_add_hcd(hcd, 0, 0);
 206        if (retval)
 207                goto exit_connection_disable;
 208
 209        return 0;
 210
 211exit_connection_disable:
 212        gb_connection_disable(connection);
 213exit_connection_destroy:
 214        gb_connection_destroy(connection);
 215exit_usb_put:
 216        usb_put_hcd(hcd);
 217
 218        return retval;
 219}
 220
 221static void gb_usb_remove(struct gbphy_device *gbphy_dev)
 222{
 223        struct gb_usb_device *gb_usb_dev = gb_gbphy_get_data(gbphy_dev);
 224        struct gb_connection *connection = gb_usb_dev->connection;
 225        struct usb_hcd *hcd = gb_usb_device_to_hcd(gb_usb_dev);
 226
 227        usb_remove_hcd(hcd);
 228        gb_connection_disable(connection);
 229        gb_connection_destroy(connection);
 230        usb_put_hcd(hcd);
 231}
 232
 233static const struct gbphy_device_id gb_usb_id_table[] = {
 234        { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_USB) },
 235        { },
 236};
 237MODULE_DEVICE_TABLE(gbphy, gb_usb_id_table);
 238
 239static struct gbphy_driver usb_driver = {
 240        .name           = "usb",
 241        .probe          = gb_usb_probe,
 242        .remove         = gb_usb_remove,
 243        .id_table       = gb_usb_id_table,
 244};
 245
 246module_gbphy_driver(usb_driver);
 247MODULE_LICENSE("GPL v2");
 248