1df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez/*
2df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8])
3df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * Notification EndPoint support
4df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
5df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * Copyright (C) 2006 Intel Corporation
6df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
7df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
8df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * This program is free software; you can redistribute it and/or
9df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * modify it under the terms of the GNU General Public License version
10df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * 2 as published by the Free Software Foundation.
11df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
12df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * This program is distributed in the hope that it will be useful,
13df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * but WITHOUT ANY WARRANTY; without even the implied warranty of
14df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * GNU General Public License for more details.
16df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
17df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * You should have received a copy of the GNU General Public License
18df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * along with this program; if not, write to the Free Software
19df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * 02110-1301, USA.
21df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
22df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
23df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * This part takes care of getting the notification from the hw
24df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * only and dispatching through wusbwad into
25df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * wa_notif_dispatch. Handling is done there.
26df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
27df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * WA notifications are limited in size; most of them are three or
28df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * four bytes long, and the longest is the HWA Device Notification,
29df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * which would not exceed 38 bytes (DNs are limited in payload to 32
30df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * bytes plus 3 bytes header (WUSB1.0[7.6p2]), plus 3 bytes HWA
31df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * header (WUSB1.0[8.5.4.2]).
32df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
33df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * It is not clear if more than one Device Notification can be packed
34df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * in a HWA Notification, I assume no because of the wording in
35df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * WUSB1.0[8.5.4.2]. In any case, the bigger any notification could
36df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * get is 256 bytes (as the bLength field is a byte).
37df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
38df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * So what we do is we have this buffer and read into it; when a
39df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * notification arrives we schedule work to a specific, single thread
40df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * workqueue (so notifications are serialized) and copy the
41df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * notification data. After scheduling the work, we rearm the read from
42df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * the notification endpoint.
43df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
44df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * Entry points here are:
45df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
46df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * wa_nep_[create|destroy]()   To initialize/release this subsystem
47df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
48df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * wa_nep_cb()                 Callback for the notification
49df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *                                endpoint; when data is ready, this
50df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *                                does the dispatching.
51df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez */
52df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez#include <linux/workqueue.h>
53df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez#include <linux/ctype.h>
545a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
55e43ace891229607c43d35597cbba77c2e40f48d4David Vrabel
56df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez#include "wa-hc.h"
57df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez#include "wusbhc.h"
58df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez
59df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez/* Structure for queueing notifications to the workqueue */
60df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalezstruct wa_notif_work {
61df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	struct work_struct work;
62df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	struct wahc *wa;
63df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	size_t size;
64df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	u8 data[];
65df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez};
66df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez
67df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez/*
68df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * Process incoming notifications from the WA's Notification EndPoint
69df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * [the wuswad daemon, basically]
70df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
71df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * @_nw:	Pointer to a descriptor which has the pointer to the
72df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * 		@wa, the size of the buffer and the work queue
73df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * 		structure (so we can free all when done).
74df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * @returns     0 if ok, < 0 errno code on error.
75df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
76df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * All notifications follow the same format; they need to start with a
77df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * 'struct wa_notif_hdr' header, so it is easy to parse through
78df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * them. We just break the buffer in individual notifications (the
79df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * standard doesn't say if it can be done or is forbidden, so we are
80df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * cautious) and dispatch each.
81df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
82df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * So the handling layers are is:
83df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
84df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *   WA specific notification (from NEP)
85df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *      Device Notification Received -> wa_handle_notif_dn()
86df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *        WUSB Device notification generic handling
87df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *      BPST Adjustment -> wa_handle_notif_bpst_adj()
88df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *      ... -> ...
89df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
90df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * @wa has to be referenced
91df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez */
92df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalezstatic void wa_notif_dispatch(struct work_struct *ws)
93df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez{
94df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	void *itr;
95df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	u8 missing = 0;
96df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	struct wa_notif_work *nw = container_of(ws, struct wa_notif_work, work);
97df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	struct wahc *wa = nw->wa;
98df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	struct wa_notif_hdr *notif_hdr;
99df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	size_t size;
100df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez
101df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	struct device *dev = &wa->usb_iface->dev;
102df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez
103df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez#if 0
104df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	/* FIXME: need to check for this??? */
105df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	if (usb_hcd->state == HC_STATE_QUIESCING)	/* Going down? */
106df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		goto out;				/* screw it */
107df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez#endif
108df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	atomic_dec(&wa->notifs_queued);		/* Throttling ctl */
109df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	dev = &wa->usb_iface->dev;
110df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	size = nw->size;
111df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	itr = nw->data;
112df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez
113df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	while (size) {
114df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		if (size < sizeof(*notif_hdr)) {
115df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			missing = sizeof(*notif_hdr) - size;
116df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			goto exhausted_buffer;
117df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		}
118df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		notif_hdr = itr;
119df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		if (size < notif_hdr->bLength)
120df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			goto exhausted_buffer;
121df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		itr += notif_hdr->bLength;
122df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		size -= notif_hdr->bLength;
123df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		/* Dispatch the notification [don't use itr or size!] */
124df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		switch (notif_hdr->bNotifyType) {
125df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		case HWA_NOTIF_DN: {
126df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			struct hwa_notif_dn *hwa_dn;
127df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			hwa_dn = container_of(notif_hdr, struct hwa_notif_dn,
128df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez					      hdr);
129df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			wusbhc_handle_dn(wa->wusb, hwa_dn->bSourceDeviceAddr,
130df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez					 hwa_dn->dndata,
131df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez					 notif_hdr->bLength - sizeof(*hwa_dn));
132df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			break;
133df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		}
134df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		case WA_NOTIF_TRANSFER:
135df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			wa_handle_notif_xfer(wa, notif_hdr);
136df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			break;
137df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		case DWA_NOTIF_RWAKE:
138df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		case DWA_NOTIF_PORTSTATUS:
139df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		case HWA_NOTIF_BPST_ADJ:
140df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			/* FIXME: unimplemented WA NOTIFs */
141df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			/* fallthru */
142df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		default:
143e43ace891229607c43d35597cbba77c2e40f48d4David Vrabel			dev_err(dev, "HWA: unknown notification 0x%x, "
144e43ace891229607c43d35597cbba77c2e40f48d4David Vrabel				"%zu bytes; discarding\n",
145e43ace891229607c43d35597cbba77c2e40f48d4David Vrabel				notif_hdr->bNotifyType,
146e43ace891229607c43d35597cbba77c2e40f48d4David Vrabel				(size_t)notif_hdr->bLength);
147df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			break;
148df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		}
149df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	}
150df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalezout:
151df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	wa_put(wa);
152df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	kfree(nw);
153df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	return;
154df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez
155df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	/* THIS SHOULD NOT HAPPEN
156df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	 *
157df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	 * Buffer exahusted with partial data remaining; just warn and
158df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	 * discard the data, as this should not happen.
159df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	 */
160df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalezexhausted_buffer:
161df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	dev_warn(dev, "HWA: device sent short notification, "
162df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		 "%d bytes missing; discarding %d bytes.\n",
163df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		 missing, (int)size);
164df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	goto out;
165df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez}
166df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez
167df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez/*
168df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * Deliver incoming WA notifications to the wusbwa workqueue
169df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
170df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * @wa:	Pointer the Wire Adapter Controller Data Streaming
171df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *              instance (part of an 'struct usb_hcd').
172df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * @size:       Size of the received buffer
173df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * @returns     0 if ok, < 0 errno code on error.
174df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
175df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * The input buffer is @wa->nep_buffer, with @size bytes
176df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * (guaranteed to fit in the allocated space,
177df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * @wa->nep_buffer_size).
178df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez */
179df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalezstatic int wa_nep_queue(struct wahc *wa, size_t size)
180df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez{
181df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	int result = 0;
182df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	struct device *dev = &wa->usb_iface->dev;
183df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	struct wa_notif_work *nw;
184df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez
185df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	/* dev_fnstart(dev, "(wa %p, size %zu)\n", wa, size); */
186df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	BUG_ON(size > wa->nep_buffer_size);
187df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	if (size == 0)
188df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		goto out;
189df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	if (atomic_read(&wa->notifs_queued) > 200) {
190df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		if (printk_ratelimit())
191df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			dev_err(dev, "Too many notifications queued, "
192df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez				"throttling back\n");
193df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		goto out;
194df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	}
195df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	nw = kzalloc(sizeof(*nw) + size, GFP_ATOMIC);
196df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	if (nw == NULL) {
197df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		if (printk_ratelimit())
198df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			dev_err(dev, "No memory to queue notification\n");
199df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		goto out;
200df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	}
201df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	INIT_WORK(&nw->work, wa_notif_dispatch);
202df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	nw->wa = wa_get(wa);
203df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	nw->size = size;
204df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	memcpy(nw->data, wa->nep_buffer, size);
205df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	atomic_inc(&wa->notifs_queued);		/* Throttling ctl */
206df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	queue_work(wusbd, &nw->work);
207df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalezout:
208df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	/* dev_fnend(dev, "(wa %p, size %zu) = result\n", wa, size, result); */
209df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	return result;
210df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez}
211df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez
212df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez/*
213df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * Callback for the notification event endpoint
214df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
215df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * Check's that everything is fine and then passes the data to be
216df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * queued to the workqueue.
217df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez */
218df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalezstatic void wa_nep_cb(struct urb *urb)
219df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez{
220df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	int result;
221df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	struct wahc *wa = urb->context;
222df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	struct device *dev = &wa->usb_iface->dev;
223df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez
224df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	switch (result = urb->status) {
225df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	case 0:
226df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		result = wa_nep_queue(wa, urb->actual_length);
227df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		if (result < 0)
228df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			dev_err(dev, "NEP: unable to process notification(s): "
229df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez				"%d\n", result);
230df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		break;
231df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	case -ECONNRESET:	/* Not an error, but a controlled situation; */
232df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	case -ENOENT:		/* (we killed the URB)...so, no broadcast */
233df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	case -ESHUTDOWN:
234df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		dev_dbg(dev, "NEP: going down %d\n", urb->status);
235df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		goto out;
236df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	default:	/* On general errors, we retry unless it gets ugly */
237df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS,
238df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			    EDC_ERROR_TIMEFRAME)) {
239df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			dev_err(dev, "NEP: URB max acceptable errors "
240df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez				"exceeded, resetting device\n");
241df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			wa_reset_all(wa);
242df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			goto out;
243df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		}
244df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		dev_err(dev, "NEP: URB error %d\n", urb->status);
245df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	}
246df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	result = wa_nep_arm(wa, GFP_ATOMIC);
247df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	if (result < 0) {
248df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		dev_err(dev, "NEP: cannot submit URB: %d\n", result);
249df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		wa_reset_all(wa);
250df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	}
251df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalezout:
252df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	return;
253df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez}
254df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez
255df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez/*
256df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * Initialize @wa's notification and event's endpoint stuff
257df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez *
258df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * This includes the allocating the read buffer, the context ID
259df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez * allocation bitmap, the URB and submitting the URB.
260df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez */
261df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalezint wa_nep_create(struct wahc *wa, struct usb_interface *iface)
262df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez{
263df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	int result;
264df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	struct usb_endpoint_descriptor *epd;
265df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	struct usb_device *usb_dev = interface_to_usbdev(iface);
266df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	struct device *dev = &iface->dev;
267df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez
268df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	edc_init(&wa->nep_edc);
269df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	epd = &iface->cur_altsetting->endpoint[0].desc;
270df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	wa->nep_buffer_size = 1024;
271df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	wa->nep_buffer = kmalloc(wa->nep_buffer_size, GFP_KERNEL);
272df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	if (wa->nep_buffer == NULL) {
273df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		dev_err(dev, "Unable to allocate notification's read buffer\n");
274df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		goto error_nep_buffer;
275df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	}
276df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	wa->nep_urb = usb_alloc_urb(0, GFP_KERNEL);
277df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	if (wa->nep_urb == NULL) {
278df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		dev_err(dev, "Unable to allocate notification URB\n");
279df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		goto error_urb_alloc;
280df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	}
281df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	usb_fill_int_urb(wa->nep_urb, usb_dev,
282df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			 usb_rcvintpipe(usb_dev, epd->bEndpointAddress),
283df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			 wa->nep_buffer, wa->nep_buffer_size,
284df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez			 wa_nep_cb, wa, epd->bInterval);
285df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	result = wa_nep_arm(wa, GFP_KERNEL);
286df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	if (result < 0) {
287df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		dev_err(dev, "Cannot submit notification URB: %d\n", result);
288df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez		goto error_nep_arm;
289df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	}
290df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	return 0;
291df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez
292df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalezerror_nep_arm:
293df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	usb_free_urb(wa->nep_urb);
294df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalezerror_urb_alloc:
295df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	kfree(wa->nep_buffer);
296df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalezerror_nep_buffer:
297df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	return -ENOMEM;
298df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez}
299df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez
300df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalezvoid wa_nep_destroy(struct wahc *wa)
301df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez{
302df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	wa_nep_disarm(wa);
303df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	usb_free_urb(wa->nep_urb);
304df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez	kfree(wa->nep_buffer);
305df3654236e31f6cf425ed2ee5a74ceac366a7a9eInaky Perez-Gonzalez}
306