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