[go: nahoru, domu]

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