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