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