linux/drivers/staging/ozwpan/ozusbsvc1.c
<<
>>
Prefs
   1/* -----------------------------------------------------------------------------
   2 * Copyright (c) 2011 Ozmo Inc
   3 * Released under the GNU General Public License Version 2 (GPLv2).
   4 *
   5 * This file implements the protocol specific parts of the USB service for a PD.
   6 * -----------------------------------------------------------------------------
   7 */
   8#include <linux/init.h>
   9#include <linux/module.h>
  10#include <linux/timer.h>
  11#include <linux/sched.h>
  12#include <linux/netdevice.h>
  13#include <linux/errno.h>
  14#include <linux/input.h>
  15#include <asm/unaligned.h>
  16#include "ozconfig.h"
  17#include "ozprotocol.h"
  18#include "ozeltbuf.h"
  19#include "ozpd.h"
  20#include "ozproto.h"
  21#include "ozusbif.h"
  22#include "ozhcd.h"
  23#include "oztrace.h"
  24#include "ozusbsvc.h"
  25/*------------------------------------------------------------------------------
  26 */
  27#define MAX_ISOC_FIXED_DATA     (253-sizeof(struct oz_isoc_fixed))
  28/*------------------------------------------------------------------------------
  29 * Context: softirq
  30 */
  31static int oz_usb_submit_elt(struct oz_elt_buf *eb, struct oz_elt_info *ei,
  32        struct oz_usb_ctx *usb_ctx, u8 strid, u8 isoc)
  33{
  34        int ret;
  35        struct oz_elt *elt = (struct oz_elt *)ei->data;
  36        struct oz_app_hdr *app_hdr = (struct oz_app_hdr *)(elt+1);
  37        elt->type = OZ_ELT_APP_DATA;
  38        ei->app_id = OZ_APPID_USB;
  39        ei->length = elt->length + sizeof(struct oz_elt);
  40        app_hdr->app_id = OZ_APPID_USB;
  41        spin_lock_bh(&eb->lock);
  42        if (isoc == 0) {
  43                app_hdr->elt_seq_num = usb_ctx->tx_seq_num++;
  44                if (usb_ctx->tx_seq_num == 0)
  45                        usb_ctx->tx_seq_num = 1;
  46        }
  47        ret = oz_queue_elt_info(eb, isoc, strid, ei);
  48        if (ret)
  49                oz_elt_info_free(eb, ei);
  50        spin_unlock_bh(&eb->lock);
  51        return ret;
  52}
  53/*------------------------------------------------------------------------------
  54 * Context: softirq
  55 */
  56int oz_usb_get_desc_req(void *hpd, u8 req_id, u8 req_type, u8 desc_type,
  57        u8 index, u16 windex, int offset, int len)
  58{
  59        struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd;
  60        struct oz_pd *pd = usb_ctx->pd;
  61        struct oz_elt *elt;
  62        struct oz_get_desc_req *body;
  63        struct oz_elt_buf *eb = &pd->elt_buff;
  64        struct oz_elt_info *ei = oz_elt_info_alloc(&pd->elt_buff);
  65        oz_trace("    req_type = 0x%x\n", req_type);
  66        oz_trace("    desc_type = 0x%x\n", desc_type);
  67        oz_trace("    index = 0x%x\n", index);
  68        oz_trace("    windex = 0x%x\n", windex);
  69        oz_trace("    offset = 0x%x\n", offset);
  70        oz_trace("    len = 0x%x\n", len);
  71        if (len > 200)
  72                len = 200;
  73        if (ei == NULL)
  74                return -1;
  75        elt = (struct oz_elt *)ei->data;
  76        elt->length = sizeof(struct oz_get_desc_req);
  77        body = (struct oz_get_desc_req *)(elt+1);
  78        body->type = OZ_GET_DESC_REQ;
  79        body->req_id = req_id;
  80        put_unaligned(cpu_to_le16(offset), &body->offset);
  81        put_unaligned(cpu_to_le16(len), &body->size);
  82        body->req_type = req_type;
  83        body->desc_type = desc_type;
  84        body->w_index = windex;
  85        body->index = index;
  86        return oz_usb_submit_elt(eb, ei, usb_ctx, 0, 0);
  87}
  88/*------------------------------------------------------------------------------
  89 * Context: tasklet
  90 */
  91static int oz_usb_set_config_req(void *hpd, u8 req_id, u8 index)
  92{
  93        struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd;
  94        struct oz_pd *pd = usb_ctx->pd;
  95        struct oz_elt *elt;
  96        struct oz_elt_buf *eb = &pd->elt_buff;
  97        struct oz_elt_info *ei = oz_elt_info_alloc(&pd->elt_buff);
  98        struct oz_set_config_req *body;
  99        if (ei == NULL)
 100                return -1;
 101        elt = (struct oz_elt *)ei->data;
 102        elt->length = sizeof(struct oz_set_config_req);
 103        body = (struct oz_set_config_req *)(elt+1);
 104        body->type = OZ_SET_CONFIG_REQ;
 105        body->req_id = req_id;
 106        body->index = index;
 107        return oz_usb_submit_elt(eb, ei, usb_ctx, 0, 0);
 108}
 109/*------------------------------------------------------------------------------
 110 * Context: tasklet
 111 */
 112static int oz_usb_set_interface_req(void *hpd, u8 req_id, u8 index, u8 alt)
 113{
 114        struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd;
 115        struct oz_pd *pd = usb_ctx->pd;
 116        struct oz_elt *elt;
 117        struct oz_elt_buf *eb = &pd->elt_buff;
 118        struct oz_elt_info *ei = oz_elt_info_alloc(&pd->elt_buff);
 119        struct oz_set_interface_req *body;
 120        if (ei == NULL)
 121                return -1;
 122        elt = (struct oz_elt *)ei->data;
 123        elt->length = sizeof(struct oz_set_interface_req);
 124        body = (struct oz_set_interface_req *)(elt+1);
 125        body->type = OZ_SET_INTERFACE_REQ;
 126        body->req_id = req_id;
 127        body->index = index;
 128        body->alternative = alt;
 129        return oz_usb_submit_elt(eb, ei, usb_ctx, 0, 0);
 130}
 131/*------------------------------------------------------------------------------
 132 * Context: tasklet
 133 */
 134static int oz_usb_set_clear_feature_req(void *hpd, u8 req_id, u8 type,
 135                        u8 recipient, u8 index, __le16 feature)
 136{
 137        struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd;
 138        struct oz_pd *pd = usb_ctx->pd;
 139        struct oz_elt *elt;
 140        struct oz_elt_buf *eb = &pd->elt_buff;
 141        struct oz_elt_info *ei = oz_elt_info_alloc(&pd->elt_buff);
 142        struct oz_feature_req *body;
 143        if (ei == NULL)
 144                return -1;
 145        elt = (struct oz_elt *)ei->data;
 146        elt->length = sizeof(struct oz_feature_req);
 147        body = (struct oz_feature_req *)(elt+1);
 148        body->type = type;
 149        body->req_id = req_id;
 150        body->recipient = recipient;
 151        body->index = index;
 152        put_unaligned(feature, &body->feature);
 153        return oz_usb_submit_elt(eb, ei, usb_ctx, 0, 0);
 154}
 155/*------------------------------------------------------------------------------
 156 * Context: tasklet
 157 */
 158static int oz_usb_vendor_class_req(void *hpd, u8 req_id, u8 req_type,
 159        u8 request, __le16 value, __le16 index, const u8 *data, int data_len)
 160{
 161        struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd;
 162        struct oz_pd *pd = usb_ctx->pd;
 163        struct oz_elt *elt;
 164        struct oz_elt_buf *eb = &pd->elt_buff;
 165        struct oz_elt_info *ei = oz_elt_info_alloc(&pd->elt_buff);
 166        struct oz_vendor_class_req *body;
 167        if (ei == NULL)
 168                return -1;
 169        elt = (struct oz_elt *)ei->data;
 170        elt->length = sizeof(struct oz_vendor_class_req) - 1 + data_len;
 171        body = (struct oz_vendor_class_req *)(elt+1);
 172        body->type = OZ_VENDOR_CLASS_REQ;
 173        body->req_id = req_id;
 174        body->req_type = req_type;
 175        body->request = request;
 176        put_unaligned(value, &body->value);
 177        put_unaligned(index, &body->index);
 178        if (data_len)
 179                memcpy(body->data, data, data_len);
 180        return oz_usb_submit_elt(eb, ei, usb_ctx, 0, 0);
 181}
 182/*------------------------------------------------------------------------------
 183 * Context: tasklet
 184 */
 185int oz_usb_control_req(void *hpd, u8 req_id, struct usb_ctrlrequest *setup,
 186                        const u8 *data, int data_len)
 187{
 188        unsigned wvalue = le16_to_cpu(setup->wValue);
 189        unsigned windex = le16_to_cpu(setup->wIndex);
 190        unsigned wlength = le16_to_cpu(setup->wLength);
 191        int rc = 0;
 192        if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
 193                switch (setup->bRequest) {
 194                case USB_REQ_GET_DESCRIPTOR:
 195                        rc = oz_usb_get_desc_req(hpd, req_id,
 196                                setup->bRequestType, (u8)(wvalue>>8),
 197                                (u8)wvalue, setup->wIndex, 0, wlength);
 198                        break;
 199                case USB_REQ_SET_CONFIGURATION:
 200                        rc = oz_usb_set_config_req(hpd, req_id, (u8)wvalue);
 201                        break;
 202                case USB_REQ_SET_INTERFACE: {
 203                                u8 if_num = (u8)windex;
 204                                u8 alt = (u8)wvalue;
 205                                rc = oz_usb_set_interface_req(hpd, req_id,
 206                                        if_num, alt);
 207                        }
 208                        break;
 209                case USB_REQ_SET_FEATURE:
 210                        rc = oz_usb_set_clear_feature_req(hpd, req_id,
 211                                OZ_SET_FEATURE_REQ,
 212                                setup->bRequestType & 0xf, (u8)windex,
 213                                setup->wValue);
 214                        break;
 215                case USB_REQ_CLEAR_FEATURE:
 216                        rc = oz_usb_set_clear_feature_req(hpd, req_id,
 217                                OZ_CLEAR_FEATURE_REQ,
 218                                setup->bRequestType & 0xf,
 219                                (u8)windex, setup->wValue);
 220                        break;
 221                }
 222        } else {
 223                rc = oz_usb_vendor_class_req(hpd, req_id, setup->bRequestType,
 224                        setup->bRequest, setup->wValue, setup->wIndex,
 225                        data, data_len);
 226        }
 227        return rc;
 228}
 229/*------------------------------------------------------------------------------
 230 * Context: softirq
 231 */
 232int oz_usb_send_isoc(void *hpd, u8 ep_num, struct urb *urb)
 233{
 234        struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd;
 235        struct oz_pd *pd = usb_ctx->pd;
 236        struct oz_elt_buf *eb;
 237        int i;
 238        int hdr_size;
 239        u8 *data;
 240        struct usb_iso_packet_descriptor *desc;
 241
 242        if (pd->mode & OZ_F_ISOC_NO_ELTS) {
 243                for (i = 0; i < urb->number_of_packets; i++) {
 244                        u8 *data;
 245                        desc = &urb->iso_frame_desc[i];
 246                        data = ((u8 *)urb->transfer_buffer)+desc->offset;
 247                        oz_send_isoc_unit(pd, ep_num, data, desc->length);
 248                }
 249                return 0;
 250        }
 251
 252        hdr_size = sizeof(struct oz_isoc_fixed) - 1;
 253        eb = &pd->elt_buff;
 254        i = 0;
 255        while (i < urb->number_of_packets) {
 256                struct oz_elt_info *ei = oz_elt_info_alloc(eb);
 257                struct oz_elt *elt;
 258                struct oz_isoc_fixed *body;
 259                int unit_count;
 260                int unit_size;
 261                int rem;
 262                if (ei == NULL)
 263                        return -1;
 264                rem = MAX_ISOC_FIXED_DATA;
 265                elt = (struct oz_elt *)ei->data;
 266                body = (struct oz_isoc_fixed *)(elt + 1);
 267                body->type = OZ_USB_ENDPOINT_DATA;
 268                body->endpoint = ep_num;
 269                body->format = OZ_DATA_F_ISOC_FIXED;
 270                unit_size = urb->iso_frame_desc[i].length;
 271                body->unit_size = (u8)unit_size;
 272                data = ((u8 *)(elt+1)) + hdr_size;
 273                unit_count = 0;
 274                while (i < urb->number_of_packets) {
 275                        desc = &urb->iso_frame_desc[i];
 276                        if ((unit_size == desc->length) &&
 277                                (desc->length <= rem)) {
 278                                memcpy(data, ((u8 *)urb->transfer_buffer) +
 279                                        desc->offset, unit_size);
 280                                data += unit_size;
 281                                rem -= unit_size;
 282                                unit_count++;
 283                                desc->status = 0;
 284                                desc->actual_length = desc->length;
 285                                i++;
 286                        } else {
 287                                break;
 288                        }
 289                }
 290                elt->length = hdr_size + MAX_ISOC_FIXED_DATA - rem;
 291                /* Store the number of units in body->frame_number for the
 292                 * moment. This field will be correctly determined before
 293                 * the element is sent. */
 294                body->frame_number = (u8)unit_count;
 295                oz_usb_submit_elt(eb, ei, usb_ctx, ep_num,
 296                        pd->mode & OZ_F_ISOC_ANYTIME);
 297        }
 298        return 0;
 299}
 300/*------------------------------------------------------------------------------
 301 * Context: softirq-serialized
 302 */
 303static void oz_usb_handle_ep_data(struct oz_usb_ctx *usb_ctx,
 304        struct oz_usb_hdr *usb_hdr, int len)
 305{
 306        struct oz_data *data_hdr = (struct oz_data *)usb_hdr;
 307        switch (data_hdr->format) {
 308        case OZ_DATA_F_MULTIPLE_FIXED: {
 309                        struct oz_multiple_fixed *body =
 310                                (struct oz_multiple_fixed *)data_hdr;
 311                        u8 *data = body->data;
 312                        int n = (len - sizeof(struct oz_multiple_fixed)+1)
 313                                / body->unit_size;
 314                        while (n--) {
 315                                oz_hcd_data_ind(usb_ctx->hport, body->endpoint,
 316                                        data, body->unit_size);
 317                                data += body->unit_size;
 318                        }
 319                }
 320                break;
 321        case OZ_DATA_F_ISOC_FIXED: {
 322                        struct oz_isoc_fixed *body =
 323                                (struct oz_isoc_fixed *)data_hdr;
 324                        int data_len = len-sizeof(struct oz_isoc_fixed)+1;
 325                        int unit_size = body->unit_size;
 326                        u8 *data = body->data;
 327                        int count;
 328                        int i;
 329                        if (!unit_size)
 330                                break;
 331                        count = data_len/unit_size;
 332                        for (i = 0; i < count; i++) {
 333                                oz_hcd_data_ind(usb_ctx->hport,
 334                                        body->endpoint, data, unit_size);
 335                                data += unit_size;
 336                        }
 337                }
 338                break;
 339        }
 340
 341}
 342/*------------------------------------------------------------------------------
 343 * This is called when the PD has received a USB element. The type of element
 344 * is determined and is then passed to an appropriate handler function.
 345 * Context: softirq-serialized
 346 */
 347void oz_usb_rx(struct oz_pd *pd, struct oz_elt *elt)
 348{
 349        struct oz_usb_hdr *usb_hdr = (struct oz_usb_hdr *)(elt + 1);
 350        struct oz_usb_ctx *usb_ctx;
 351
 352        spin_lock_bh(&pd->app_lock[OZ_APPID_USB-1]);
 353        usb_ctx = (struct oz_usb_ctx *)pd->app_ctx[OZ_APPID_USB-1];
 354        if (usb_ctx)
 355                oz_usb_get(usb_ctx);
 356        spin_unlock_bh(&pd->app_lock[OZ_APPID_USB-1]);
 357        if (usb_ctx == NULL)
 358                return; /* Context has gone so nothing to do. */
 359        if (usb_ctx->stopped)
 360                goto done;
 361        /* If sequence number is non-zero then check it is not a duplicate.
 362         * Zero sequence numbers are always accepted.
 363         */
 364        if (usb_hdr->elt_seq_num != 0) {
 365                if (((usb_ctx->rx_seq_num - usb_hdr->elt_seq_num) & 0x80) == 0)
 366                        /* Reject duplicate element. */
 367                        goto done;
 368        }
 369        usb_ctx->rx_seq_num = usb_hdr->elt_seq_num;
 370        switch (usb_hdr->type) {
 371        case OZ_GET_DESC_RSP: {
 372                        struct oz_get_desc_rsp *body =
 373                                (struct oz_get_desc_rsp *)usb_hdr;
 374                        int data_len = elt->length -
 375                                        sizeof(struct oz_get_desc_rsp) + 1;
 376                        u16 offs = le16_to_cpu(get_unaligned(&body->offset));
 377                        u16 total_size =
 378                                le16_to_cpu(get_unaligned(&body->total_size));
 379                        oz_trace("USB_REQ_GET_DESCRIPTOR - cnf\n");
 380                        oz_hcd_get_desc_cnf(usb_ctx->hport, body->req_id,
 381                                        body->rcode, body->data,
 382                                        data_len, offs, total_size);
 383                }
 384                break;
 385        case OZ_SET_CONFIG_RSP: {
 386                        struct oz_set_config_rsp *body =
 387                                (struct oz_set_config_rsp *)usb_hdr;
 388                        oz_hcd_control_cnf(usb_ctx->hport, body->req_id,
 389                                body->rcode, NULL, 0);
 390                }
 391                break;
 392        case OZ_SET_INTERFACE_RSP: {
 393                        struct oz_set_interface_rsp *body =
 394                                (struct oz_set_interface_rsp *)usb_hdr;
 395                        oz_hcd_control_cnf(usb_ctx->hport,
 396                                body->req_id, body->rcode, NULL, 0);
 397                }
 398                break;
 399        case OZ_VENDOR_CLASS_RSP: {
 400                        struct oz_vendor_class_rsp *body =
 401                                (struct oz_vendor_class_rsp *)usb_hdr;
 402                        oz_hcd_control_cnf(usb_ctx->hport, body->req_id,
 403                                body->rcode, body->data, elt->length-
 404                                sizeof(struct oz_vendor_class_rsp)+1);
 405                }
 406                break;
 407        case OZ_USB_ENDPOINT_DATA:
 408                oz_usb_handle_ep_data(usb_ctx, usb_hdr, elt->length);
 409                break;
 410        }
 411done:
 412        oz_usb_put(usb_ctx);
 413}
 414/*------------------------------------------------------------------------------
 415 * Context: softirq, process
 416 */
 417void oz_usb_farewell(struct oz_pd *pd, u8 ep_num, u8 *data, u8 len)
 418{
 419        struct oz_usb_ctx *usb_ctx;
 420        spin_lock_bh(&pd->app_lock[OZ_APPID_USB-1]);
 421        usb_ctx = (struct oz_usb_ctx *)pd->app_ctx[OZ_APPID_USB-1];
 422        if (usb_ctx)
 423                oz_usb_get(usb_ctx);
 424        spin_unlock_bh(&pd->app_lock[OZ_APPID_USB-1]);
 425        if (usb_ctx == NULL)
 426                return; /* Context has gone so nothing to do. */
 427        if (!usb_ctx->stopped) {
 428                oz_trace("Farewell indicated ep = 0x%x\n", ep_num);
 429                oz_hcd_data_ind(usb_ctx->hport, ep_num, data, len);
 430        }
 431        oz_usb_put(usb_ctx);
 432}
 433