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