1*4882a593Smuzhiyun /*===========================================================================
2*4882a593Smuzhiyun FILE:
3*4882a593Smuzhiyun QMIDevice.c
4*4882a593Smuzhiyun
5*4882a593Smuzhiyun DESCRIPTION:
6*4882a593Smuzhiyun Functions related to the QMI interface device
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun FUNCTIONS:
9*4882a593Smuzhiyun Generic functions
10*4882a593Smuzhiyun IsDeviceValid
11*4882a593Smuzhiyun PrintHex
12*4882a593Smuzhiyun GobiSetDownReason
13*4882a593Smuzhiyun GobiClearDownReason
14*4882a593Smuzhiyun GobiTestDownReason
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun Driver level asynchronous read functions
17*4882a593Smuzhiyun ResubmitIntURB
18*4882a593Smuzhiyun ReadCallback
19*4882a593Smuzhiyun IntCallback
20*4882a593Smuzhiyun StartRead
21*4882a593Smuzhiyun KillRead
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun Internal read/write functions
24*4882a593Smuzhiyun ReadAsync
25*4882a593Smuzhiyun UpSem
26*4882a593Smuzhiyun ReadSync
27*4882a593Smuzhiyun WriteSyncCallback
28*4882a593Smuzhiyun WriteSync
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun Internal memory management functions
31*4882a593Smuzhiyun GetClientID
32*4882a593Smuzhiyun ReleaseClientID
33*4882a593Smuzhiyun FindClientMem
34*4882a593Smuzhiyun AddToReadMemList
35*4882a593Smuzhiyun PopFromReadMemList
36*4882a593Smuzhiyun AddToNotifyList
37*4882a593Smuzhiyun NotifyAndPopNotifyList
38*4882a593Smuzhiyun AddToURBList
39*4882a593Smuzhiyun PopFromURBList
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun Internal userspace wrapper functions
42*4882a593Smuzhiyun UserspaceunlockedIOCTL
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun Userspace wrappers
45*4882a593Smuzhiyun UserspaceOpen
46*4882a593Smuzhiyun UserspaceIOCTL
47*4882a593Smuzhiyun UserspaceClose
48*4882a593Smuzhiyun UserspaceRead
49*4882a593Smuzhiyun UserspaceWrite
50*4882a593Smuzhiyun UserspacePoll
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun Initializer and destructor
53*4882a593Smuzhiyun RegisterQMIDevice
54*4882a593Smuzhiyun DeregisterQMIDevice
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun Driver level client management
57*4882a593Smuzhiyun QMIReady
58*4882a593Smuzhiyun QMIWDSCallback
59*4882a593Smuzhiyun SetupQMIWDSCallback
60*4882a593Smuzhiyun QMIDMSGetMEID
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun Copyright (c) 2011, Code Aurora Forum. All rights reserved.
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun Redistribution and use in source and binary forms, with or without
65*4882a593Smuzhiyun modification, are permitted provided that the following conditions are met:
66*4882a593Smuzhiyun * Redistributions of source code must retain the above copyright
67*4882a593Smuzhiyun notice, this list of conditions and the following disclaimer.
68*4882a593Smuzhiyun * Redistributions in binary form must reproduce the above copyright
69*4882a593Smuzhiyun notice, this list of conditions and the following disclaimer in the
70*4882a593Smuzhiyun documentation and/or other materials provided with the distribution.
71*4882a593Smuzhiyun * Neither the name of Code Aurora Forum nor
72*4882a593Smuzhiyun the names of its contributors may be used to endorse or promote
73*4882a593Smuzhiyun products derived from this software without specific prior written
74*4882a593Smuzhiyun permission.
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
78*4882a593Smuzhiyun AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
79*4882a593Smuzhiyun IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
80*4882a593Smuzhiyun ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
81*4882a593Smuzhiyun LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
82*4882a593Smuzhiyun CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
83*4882a593Smuzhiyun SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
84*4882a593Smuzhiyun INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
85*4882a593Smuzhiyun CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
86*4882a593Smuzhiyun ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
87*4882a593Smuzhiyun POSSIBILITY OF SUCH DAMAGE.
88*4882a593Smuzhiyun ===========================================================================*/
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun //---------------------------------------------------------------------------
91*4882a593Smuzhiyun // Include Files
92*4882a593Smuzhiyun //---------------------------------------------------------------------------
93*4882a593Smuzhiyun #include <asm/unaligned.h>
94*4882a593Smuzhiyun #include <linux/module.h>
95*4882a593Smuzhiyun #include <linux/usb/cdc.h>
96*4882a593Smuzhiyun #include <linux/usb.h>
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun //-----------------------------------------------------------------------------
99*4882a593Smuzhiyun // Definitions
100*4882a593Smuzhiyun //-----------------------------------------------------------------------------
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun #define __QUEC_INCLUDE_QMI_C__
103*4882a593Smuzhiyun #include "QMI.c"
104*4882a593Smuzhiyun #define __QUECTEL_INTER__
105*4882a593Smuzhiyun #include "QMIDevice.h"
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun #if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,22 ))
108*4882a593Smuzhiyun static int s_interval;
109*4882a593Smuzhiyun #endif
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun #if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,14 ))
112*4882a593Smuzhiyun #include <linux/devfs_fs_kernel.h>
113*4882a593Smuzhiyun static char devfs_name[32];
device_create(struct class * class,struct device * parent,dev_t devt,const char * fmt,...)114*4882a593Smuzhiyun static int device_create(struct class *class, struct device *parent, dev_t devt, const char *fmt, ...)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun va_list vargs;
117*4882a593Smuzhiyun struct class_device *class_dev;
118*4882a593Smuzhiyun int err;
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun va_start(vargs, fmt);
121*4882a593Smuzhiyun vsnprintf(devfs_name, sizeof(devfs_name), fmt, vargs);
122*4882a593Smuzhiyun va_end(vargs);
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun class_dev = class_device_create(class, devt, parent, "%s", devfs_name);
125*4882a593Smuzhiyun if (IS_ERR(class_dev)) {
126*4882a593Smuzhiyun err = PTR_ERR(class_dev);
127*4882a593Smuzhiyun goto out;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun err = devfs_mk_cdev(devt, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP, devfs_name);
131*4882a593Smuzhiyun if (err) {
132*4882a593Smuzhiyun class_device_destroy(class, devt);
133*4882a593Smuzhiyun goto out;
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun return 0;
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun out:
139*4882a593Smuzhiyun return err;
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun
device_destroy(struct class * class,dev_t devt)142*4882a593Smuzhiyun static void device_destroy(struct class *class, dev_t devt)
143*4882a593Smuzhiyun {
144*4882a593Smuzhiyun class_device_destroy(class, devt);
145*4882a593Smuzhiyun devfs_remove(devfs_name);
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun #endif
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun #ifdef CONFIG_PM
150*4882a593Smuzhiyun // Prototype to GobiNetSuspend function
151*4882a593Smuzhiyun int QuecGobiNetSuspend(
152*4882a593Smuzhiyun struct usb_interface * pIntf,
153*4882a593Smuzhiyun pm_message_t powerEvent );
154*4882a593Smuzhiyun #endif /* CONFIG_PM */
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun // IOCTL to generate a client ID for this service type
157*4882a593Smuzhiyun #define IOCTL_QMI_GET_SERVICE_FILE 0x8BE0 + 1
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun // IOCTL to get the VIDPID of the device
160*4882a593Smuzhiyun #define IOCTL_QMI_GET_DEVICE_VIDPID 0x8BE0 + 2
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun // IOCTL to get the MEID of the device
163*4882a593Smuzhiyun #define IOCTL_QMI_GET_DEVICE_MEID 0x8BE0 + 3
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun #define IOCTL_QMI_RELEASE_SERVICE_FILE_IOCTL (0x8BE0 + 4)
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun // CDC GET_ENCAPSULATED_RESPONSE packet
168*4882a593Smuzhiyun #define CDC_GET_ENCAPSULATED_RESPONSE_LE 0x01A1ll
169*4882a593Smuzhiyun #define CDC_GET_ENCAPSULATED_RESPONSE_BE 0xA101000000000000ll
170*4882a593Smuzhiyun /* The following masks filter the common part of the encapsulated response
171*4882a593Smuzhiyun * packet value for Gobi and QMI devices, ie. ignore usb interface number
172*4882a593Smuzhiyun */
173*4882a593Smuzhiyun #define CDC_RSP_MASK_BE 0xFFFFFFFF00FFFFFFll
174*4882a593Smuzhiyun #define CDC_RSP_MASK_LE 0xFFFFFFE0FFFFFFFFll
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun static const int i = 1;
177*4882a593Smuzhiyun #define is_bigendian() ( (*(char*)&i) == 0 )
178*4882a593Smuzhiyun #define CDC_GET_ENCAPSULATED_RESPONSE(pcdcrsp, pmask)\
179*4882a593Smuzhiyun {\
180*4882a593Smuzhiyun *pcdcrsp = is_bigendian() ? CDC_GET_ENCAPSULATED_RESPONSE_BE \
181*4882a593Smuzhiyun : CDC_GET_ENCAPSULATED_RESPONSE_LE ; \
182*4882a593Smuzhiyun *pmask = is_bigendian() ? CDC_RSP_MASK_BE \
183*4882a593Smuzhiyun : CDC_RSP_MASK_LE; \
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun // CDC CONNECTION_SPEED_CHANGE indication packet
187*4882a593Smuzhiyun #define CDC_CONNECTION_SPEED_CHANGE_LE 0x2AA1ll
188*4882a593Smuzhiyun #define CDC_CONNECTION_SPEED_CHANGE_BE 0xA12A000000000000ll
189*4882a593Smuzhiyun /* The following masks filter the common part of the connection speed change
190*4882a593Smuzhiyun * packet value for Gobi and QMI devices
191*4882a593Smuzhiyun */
192*4882a593Smuzhiyun #define CDC_CONNSPD_MASK_BE 0xFFFFFFFFFFFF7FFFll
193*4882a593Smuzhiyun #define CDC_CONNSPD_MASK_LE 0XFFF7FFFFFFFFFFFFll
194*4882a593Smuzhiyun #define CDC_GET_CONNECTION_SPEED_CHANGE(pcdccscp, pmask)\
195*4882a593Smuzhiyun {\
196*4882a593Smuzhiyun *pcdccscp = is_bigendian() ? CDC_CONNECTION_SPEED_CHANGE_BE \
197*4882a593Smuzhiyun : CDC_CONNECTION_SPEED_CHANGE_LE ; \
198*4882a593Smuzhiyun *pmask = is_bigendian() ? CDC_CONNSPD_MASK_BE \
199*4882a593Smuzhiyun : CDC_CONNSPD_MASK_LE; \
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun #define SET_CONTROL_LINE_STATE_REQUEST_TYPE 0x21
203*4882a593Smuzhiyun #define SET_CONTROL_LINE_STATE_REQUEST 0x22
204*4882a593Smuzhiyun #define CONTROL_DTR 0x01
205*4882a593Smuzhiyun #define CONTROL_RTS 0x02
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun /*=========================================================================*/
208*4882a593Smuzhiyun // UserspaceQMIFops
209*4882a593Smuzhiyun // QMI device's userspace file operations
210*4882a593Smuzhiyun /*=========================================================================*/
211*4882a593Smuzhiyun static struct file_operations UserspaceQMIFops =
212*4882a593Smuzhiyun {
213*4882a593Smuzhiyun .owner = THIS_MODULE,
214*4882a593Smuzhiyun .read = UserspaceRead,
215*4882a593Smuzhiyun .write = UserspaceWrite,
216*4882a593Smuzhiyun #ifdef CONFIG_COMPAT
217*4882a593Smuzhiyun .compat_ioctl = UserspaceunlockedIOCTL,
218*4882a593Smuzhiyun #endif
219*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION( 2,6,36 ))
220*4882a593Smuzhiyun .unlocked_ioctl = UserspaceunlockedIOCTL,
221*4882a593Smuzhiyun #else
222*4882a593Smuzhiyun .ioctl = UserspaceIOCTL,
223*4882a593Smuzhiyun #endif
224*4882a593Smuzhiyun .open = UserspaceOpen,
225*4882a593Smuzhiyun #ifdef quectel_no_for_each_process
226*4882a593Smuzhiyun .release = UserspaceClose,
227*4882a593Smuzhiyun #else
228*4882a593Smuzhiyun .flush = UserspaceClose,
229*4882a593Smuzhiyun #endif
230*4882a593Smuzhiyun .poll = UserspacePoll,
231*4882a593Smuzhiyun };
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun /*=========================================================================*/
234*4882a593Smuzhiyun // Generic functions
235*4882a593Smuzhiyun /*=========================================================================*/
QMIXactionIDGet(sGobiUSBNet * pDev)236*4882a593Smuzhiyun static u8 QMIXactionIDGet( sGobiUSBNet *pDev)
237*4882a593Smuzhiyun {
238*4882a593Smuzhiyun u8 transactionID;
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun if( 0 == (transactionID = atomic_add_return( 1, &pDev->mQMIDev.mQMICTLTransactionID)) )
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun transactionID = atomic_add_return( 1, &pDev->mQMIDev.mQMICTLTransactionID );
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun
245*4882a593Smuzhiyun #if 1 //free these ununsed qmi response, or when these transactionID re-used, they will be regarded as qmi response of the qmi request that have same transactionID
246*4882a593Smuzhiyun if (transactionID) {
247*4882a593Smuzhiyun unsigned long flags;
248*4882a593Smuzhiyun void * pReadBuffer;
249*4882a593Smuzhiyun u16 readBufferSize;
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
252*4882a593Smuzhiyun while (PopFromReadMemList( pDev,
253*4882a593Smuzhiyun QMICTL,
254*4882a593Smuzhiyun transactionID,
255*4882a593Smuzhiyun &pReadBuffer,
256*4882a593Smuzhiyun &readBufferSize ) == true)
257*4882a593Smuzhiyun {
258*4882a593Smuzhiyun kfree( pReadBuffer );
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun #endif
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun return transactionID;
265*4882a593Smuzhiyun }
266*4882a593Smuzhiyun
GetEndpoint(struct usb_interface * pintf,int type,int dir)267*4882a593Smuzhiyun static struct usb_endpoint_descriptor *GetEndpoint(
268*4882a593Smuzhiyun struct usb_interface *pintf,
269*4882a593Smuzhiyun int type,
270*4882a593Smuzhiyun int dir )
271*4882a593Smuzhiyun {
272*4882a593Smuzhiyun int i;
273*4882a593Smuzhiyun struct usb_host_interface *iface = pintf->cur_altsetting;
274*4882a593Smuzhiyun struct usb_endpoint_descriptor *pendp;
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun for( i = 0; i < iface->desc.bNumEndpoints; i++)
277*4882a593Smuzhiyun {
278*4882a593Smuzhiyun pendp = &iface->endpoint[i].desc;
279*4882a593Smuzhiyun if( ((pendp->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == dir)
280*4882a593Smuzhiyun &&
281*4882a593Smuzhiyun (usb_endpoint_type(pendp) == type) )
282*4882a593Smuzhiyun {
283*4882a593Smuzhiyun return pendp;
284*4882a593Smuzhiyun }
285*4882a593Smuzhiyun }
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun return NULL;
288*4882a593Smuzhiyun }
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun /*===========================================================================
291*4882a593Smuzhiyun METHOD:
292*4882a593Smuzhiyun IsDeviceValid (Public Method)
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun DESCRIPTION:
295*4882a593Smuzhiyun Basic test to see if device memory is valid
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun PARAMETERS:
298*4882a593Smuzhiyun pDev [ I ] - Device specific memory
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun RETURN VALUE:
301*4882a593Smuzhiyun bool
302*4882a593Smuzhiyun ===========================================================================*/
IsDeviceValid(sGobiUSBNet * pDev)303*4882a593Smuzhiyun static bool IsDeviceValid( sGobiUSBNet * pDev )
304*4882a593Smuzhiyun {
305*4882a593Smuzhiyun if (pDev == NULL)
306*4882a593Smuzhiyun {
307*4882a593Smuzhiyun return false;
308*4882a593Smuzhiyun }
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun if (pDev->mbQMIValid == false)
311*4882a593Smuzhiyun {
312*4882a593Smuzhiyun return false;
313*4882a593Smuzhiyun }
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun return true;
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun /*===========================================================================
319*4882a593Smuzhiyun METHOD:
320*4882a593Smuzhiyun PrintHex (Public Method)
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun DESCRIPTION:
323*4882a593Smuzhiyun Print Hex data, for debug purposes
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun PARAMETERS:
326*4882a593Smuzhiyun pBuffer [ I ] - Data buffer
327*4882a593Smuzhiyun bufSize [ I ] - Size of data buffer
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun RETURN VALUE:
330*4882a593Smuzhiyun None
331*4882a593Smuzhiyun ===========================================================================*/
QuecPrintHex(void * pBuffer,u16 bufSize)332*4882a593Smuzhiyun void QuecPrintHex(
333*4882a593Smuzhiyun void * pBuffer,
334*4882a593Smuzhiyun u16 bufSize )
335*4882a593Smuzhiyun {
336*4882a593Smuzhiyun char * pPrintBuf;
337*4882a593Smuzhiyun u16 pos;
338*4882a593Smuzhiyun int status;
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun if (quec_debug != 1)
341*4882a593Smuzhiyun {
342*4882a593Smuzhiyun return;
343*4882a593Smuzhiyun }
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun pPrintBuf = kmalloc( bufSize * 3 + 1, GFP_ATOMIC );
346*4882a593Smuzhiyun if (pPrintBuf == NULL)
347*4882a593Smuzhiyun {
348*4882a593Smuzhiyun DBG( "Unable to allocate buffer\n" );
349*4882a593Smuzhiyun return;
350*4882a593Smuzhiyun }
351*4882a593Smuzhiyun memset( pPrintBuf, 0 , bufSize * 3 + 1 );
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun for (pos = 0; pos < bufSize; pos++)
354*4882a593Smuzhiyun {
355*4882a593Smuzhiyun status = snprintf( (pPrintBuf + (pos * 3)),
356*4882a593Smuzhiyun 4,
357*4882a593Smuzhiyun "%02X ",
358*4882a593Smuzhiyun *(u8 *)(pBuffer + pos) );
359*4882a593Smuzhiyun if (status != 3)
360*4882a593Smuzhiyun {
361*4882a593Smuzhiyun DBG( "snprintf error %d\n", status );
362*4882a593Smuzhiyun kfree( pPrintBuf );
363*4882a593Smuzhiyun return;
364*4882a593Smuzhiyun }
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun DBG( " : %s\n", pPrintBuf );
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun kfree( pPrintBuf );
370*4882a593Smuzhiyun pPrintBuf = NULL;
371*4882a593Smuzhiyun return;
372*4882a593Smuzhiyun }
373*4882a593Smuzhiyun
374*4882a593Smuzhiyun /*===========================================================================
375*4882a593Smuzhiyun METHOD:
376*4882a593Smuzhiyun GobiSetDownReason (Public Method)
377*4882a593Smuzhiyun
378*4882a593Smuzhiyun DESCRIPTION:
379*4882a593Smuzhiyun Sets mDownReason and turns carrier off
380*4882a593Smuzhiyun
381*4882a593Smuzhiyun PARAMETERS
382*4882a593Smuzhiyun pDev [ I ] - Device specific memory
383*4882a593Smuzhiyun reason [ I ] - Reason device is down
384*4882a593Smuzhiyun
385*4882a593Smuzhiyun RETURN VALUE:
386*4882a593Smuzhiyun None
387*4882a593Smuzhiyun ===========================================================================*/
QuecGobiSetDownReason(sGobiUSBNet * pDev,u8 reason)388*4882a593Smuzhiyun void QuecGobiSetDownReason(
389*4882a593Smuzhiyun sGobiUSBNet * pDev,
390*4882a593Smuzhiyun u8 reason )
391*4882a593Smuzhiyun {
392*4882a593Smuzhiyun DBG("%s reason=%d, mDownReason=%x\n", __func__, reason, (unsigned)pDev->mDownReason);
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun #ifdef QUECTEL_WWAN_QMAP
395*4882a593Smuzhiyun if (reason == NO_NDIS_CONNECTION)
396*4882a593Smuzhiyun return;
397*4882a593Smuzhiyun #endif
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun set_bit( reason, &pDev->mDownReason );
400*4882a593Smuzhiyun
401*4882a593Smuzhiyun netif_carrier_off( pDev->mpNetDev->net );
402*4882a593Smuzhiyun }
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun /*===========================================================================
405*4882a593Smuzhiyun METHOD:
406*4882a593Smuzhiyun GobiClearDownReason (Public Method)
407*4882a593Smuzhiyun
408*4882a593Smuzhiyun DESCRIPTION:
409*4882a593Smuzhiyun Clear mDownReason and may turn carrier on
410*4882a593Smuzhiyun
411*4882a593Smuzhiyun PARAMETERS
412*4882a593Smuzhiyun pDev [ I ] - Device specific memory
413*4882a593Smuzhiyun reason [ I ] - Reason device is no longer down
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun RETURN VALUE:
416*4882a593Smuzhiyun None
417*4882a593Smuzhiyun ===========================================================================*/
QuecGobiClearDownReason(sGobiUSBNet * pDev,u8 reason)418*4882a593Smuzhiyun void QuecGobiClearDownReason(
419*4882a593Smuzhiyun sGobiUSBNet * pDev,
420*4882a593Smuzhiyun u8 reason )
421*4882a593Smuzhiyun {
422*4882a593Smuzhiyun clear_bit( reason, &pDev->mDownReason );
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun DBG("%s reason=%d, mDownReason=%x\n", __func__, reason, (unsigned)pDev->mDownReason);
425*4882a593Smuzhiyun #if 0 //(LINUX_VERSION_CODE >= KERNEL_VERSION( 3,11,0 ))
426*4882a593Smuzhiyun netif_carrier_on( pDev->mpNetDev->net );
427*4882a593Smuzhiyun #else
428*4882a593Smuzhiyun if (pDev->mDownReason == 0)
429*4882a593Smuzhiyun {
430*4882a593Smuzhiyun #ifdef QUECTEL_WWAN_QMAP
431*4882a593Smuzhiyun if (pDev->qmap_mode && !pDev->link_state)
432*4882a593Smuzhiyun ;
433*4882a593Smuzhiyun else
434*4882a593Smuzhiyun #endif
435*4882a593Smuzhiyun netif_carrier_on( pDev->mpNetDev->net );
436*4882a593Smuzhiyun }
437*4882a593Smuzhiyun #endif
438*4882a593Smuzhiyun }
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun /*===========================================================================
441*4882a593Smuzhiyun METHOD:
442*4882a593Smuzhiyun GobiTestDownReason (Public Method)
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun DESCRIPTION:
445*4882a593Smuzhiyun Test mDownReason and returns whether reason is set
446*4882a593Smuzhiyun
447*4882a593Smuzhiyun PARAMETERS
448*4882a593Smuzhiyun pDev [ I ] - Device specific memory
449*4882a593Smuzhiyun reason [ I ] - Reason device is down
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun RETURN VALUE:
452*4882a593Smuzhiyun bool
453*4882a593Smuzhiyun ===========================================================================*/
QuecGobiTestDownReason(sGobiUSBNet * pDev,u8 reason)454*4882a593Smuzhiyun bool QuecGobiTestDownReason(
455*4882a593Smuzhiyun sGobiUSBNet * pDev,
456*4882a593Smuzhiyun u8 reason )
457*4882a593Smuzhiyun {
458*4882a593Smuzhiyun return test_bit( reason, &pDev->mDownReason );
459*4882a593Smuzhiyun }
460*4882a593Smuzhiyun
461*4882a593Smuzhiyun /*=========================================================================*/
462*4882a593Smuzhiyun // Driver level asynchronous read functions
463*4882a593Smuzhiyun /*=========================================================================*/
464*4882a593Smuzhiyun
465*4882a593Smuzhiyun /*===========================================================================
466*4882a593Smuzhiyun METHOD:
467*4882a593Smuzhiyun ResubmitIntURB (Public Method)
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun DESCRIPTION:
470*4882a593Smuzhiyun Resubmit interrupt URB, re-using same values
471*4882a593Smuzhiyun
472*4882a593Smuzhiyun PARAMETERS
473*4882a593Smuzhiyun pIntURB [ I ] - Interrupt URB
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun RETURN VALUE:
476*4882a593Smuzhiyun int - 0 for success
477*4882a593Smuzhiyun negative errno for failure
478*4882a593Smuzhiyun ===========================================================================*/
ResubmitIntURB(struct urb * pIntURB)479*4882a593Smuzhiyun static int ResubmitIntURB( struct urb * pIntURB )
480*4882a593Smuzhiyun {
481*4882a593Smuzhiyun int status;
482*4882a593Smuzhiyun int interval;
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun // Sanity test
485*4882a593Smuzhiyun if ( (pIntURB == NULL)
486*4882a593Smuzhiyun || (pIntURB->dev == NULL) )
487*4882a593Smuzhiyun {
488*4882a593Smuzhiyun return -EINVAL;
489*4882a593Smuzhiyun }
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun // Interval needs reset after every URB completion
492*4882a593Smuzhiyun #if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,22 ))
493*4882a593Smuzhiyun interval = max((int)(pIntURB->ep->desc.bInterval),
494*4882a593Smuzhiyun (pIntURB->dev->speed == USB_SPEED_HIGH) ? 7 : 3);
495*4882a593Smuzhiyun #else
496*4882a593Smuzhiyun interval = s_interval;
497*4882a593Smuzhiyun #endif
498*4882a593Smuzhiyun
499*4882a593Smuzhiyun // Reschedule interrupt URB
500*4882a593Smuzhiyun usb_fill_int_urb( pIntURB,
501*4882a593Smuzhiyun pIntURB->dev,
502*4882a593Smuzhiyun pIntURB->pipe,
503*4882a593Smuzhiyun pIntURB->transfer_buffer,
504*4882a593Smuzhiyun pIntURB->transfer_buffer_length,
505*4882a593Smuzhiyun pIntURB->complete,
506*4882a593Smuzhiyun pIntURB->context,
507*4882a593Smuzhiyun interval );
508*4882a593Smuzhiyun status = usb_submit_urb( pIntURB, GFP_ATOMIC );
509*4882a593Smuzhiyun if (status != 0)
510*4882a593Smuzhiyun {
511*4882a593Smuzhiyun DBG( "Error re-submitting Int URB %d\n", status );
512*4882a593Smuzhiyun }
513*4882a593Smuzhiyun
514*4882a593Smuzhiyun return status;
515*4882a593Smuzhiyun }
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun
518*4882a593Smuzhiyun #ifdef QUECTEL_QMI_MERGE
MergeRecQmiMsg(sQMIDev * pQMIDev,struct urb * pReadURB)519*4882a593Smuzhiyun static int MergeRecQmiMsg( sQMIDev * pQMIDev, struct urb * pReadURB )
520*4882a593Smuzhiyun {
521*4882a593Smuzhiyun sQMIMsgHeader * mHeader;
522*4882a593Smuzhiyun sQMIMsgPacket * mPacket;
523*4882a593Smuzhiyun
524*4882a593Smuzhiyun DBG( "%s called \n", __func__ );
525*4882a593Smuzhiyun mPacket = pQMIDev->mpQmiMsgPacket;
526*4882a593Smuzhiyun
527*4882a593Smuzhiyun if(pReadURB->actual_length < sizeof(sQMIMsgHeader))
528*4882a593Smuzhiyun {
529*4882a593Smuzhiyun return -1;
530*4882a593Smuzhiyun }
531*4882a593Smuzhiyun
532*4882a593Smuzhiyun mHeader = (sQMIMsgHeader *)pReadURB->transfer_buffer;
533*4882a593Smuzhiyun if(le16_to_cpu(mHeader->idenity) != MERGE_PACKET_IDENTITY || le16_to_cpu(mHeader->version) != MERGE_PACKET_VERSION || le16_to_cpu(mHeader->cur_len) > le16_to_cpu(mHeader->total_len))
534*4882a593Smuzhiyun return -1;
535*4882a593Smuzhiyun
536*4882a593Smuzhiyun if(le16_to_cpu(mHeader->cur_len) == le16_to_cpu(mHeader->total_len)) {
537*4882a593Smuzhiyun mPacket->len = le16_to_cpu(mHeader->total_len);
538*4882a593Smuzhiyun memcpy(pReadURB->transfer_buffer, pReadURB->transfer_buffer + sizeof(sQMIMsgHeader), mPacket->len);
539*4882a593Smuzhiyun pReadURB->actual_length = mPacket->len;
540*4882a593Smuzhiyun mPacket->len = 0;
541*4882a593Smuzhiyun
542*4882a593Smuzhiyun return 0;
543*4882a593Smuzhiyun }
544*4882a593Smuzhiyun
545*4882a593Smuzhiyun memcpy(mPacket->buf + mPacket->len, pReadURB->transfer_buffer + sizeof(sQMIMsgHeader), le16_to_cpu(mHeader->cur_len));
546*4882a593Smuzhiyun mPacket->len += le16_to_cpu(mHeader->cur_len);
547*4882a593Smuzhiyun
548*4882a593Smuzhiyun if (le16_to_cpu(mHeader->cur_len) < MERGE_PACKET_MAX_PAYLOAD_SIZE || mPacket->len >= le16_to_cpu(mHeader->total_len)) {
549*4882a593Smuzhiyun memcpy(pReadURB->transfer_buffer, mPacket->buf, mPacket->len);
550*4882a593Smuzhiyun pReadURB->actual_length = mPacket->len;
551*4882a593Smuzhiyun mPacket->len = 0;
552*4882a593Smuzhiyun return 0;
553*4882a593Smuzhiyun }
554*4882a593Smuzhiyun
555*4882a593Smuzhiyun return -1;
556*4882a593Smuzhiyun }
557*4882a593Smuzhiyun #endif
558*4882a593Smuzhiyun
559*4882a593Smuzhiyun /*===========================================================================
560*4882a593Smuzhiyun METHOD:
561*4882a593Smuzhiyun ReadCallback (Public Method)
562*4882a593Smuzhiyun
563*4882a593Smuzhiyun DESCRIPTION:
564*4882a593Smuzhiyun Put the data in storage and notify anyone waiting for data
565*4882a593Smuzhiyun
566*4882a593Smuzhiyun PARAMETERS
567*4882a593Smuzhiyun pReadURB [ I ] - URB this callback is run for
568*4882a593Smuzhiyun
569*4882a593Smuzhiyun RETURN VALUE:
570*4882a593Smuzhiyun None
571*4882a593Smuzhiyun ===========================================================================*/
572*4882a593Smuzhiyun #if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,18 ))
ReadCallback(struct urb * pReadURB)573*4882a593Smuzhiyun static void ReadCallback( struct urb * pReadURB )
574*4882a593Smuzhiyun #else
575*4882a593Smuzhiyun static void ReadCallback(struct urb *pReadURB, struct pt_regs *regs)
576*4882a593Smuzhiyun #endif
577*4882a593Smuzhiyun {
578*4882a593Smuzhiyun int result;
579*4882a593Smuzhiyun u16 clientID;
580*4882a593Smuzhiyun sClientMemList * pClientMem;
581*4882a593Smuzhiyun void * pData;
582*4882a593Smuzhiyun void * pDataCopy;
583*4882a593Smuzhiyun u16 dataSize;
584*4882a593Smuzhiyun sGobiUSBNet * pDev;
585*4882a593Smuzhiyun unsigned long flags;
586*4882a593Smuzhiyun u16 transactionID;
587*4882a593Smuzhiyun
588*4882a593Smuzhiyun if (pReadURB == NULL)
589*4882a593Smuzhiyun {
590*4882a593Smuzhiyun DBG( "bad read URB\n" );
591*4882a593Smuzhiyun return;
592*4882a593Smuzhiyun }
593*4882a593Smuzhiyun
594*4882a593Smuzhiyun pDev = pReadURB->context;
595*4882a593Smuzhiyun if (IsDeviceValid( pDev ) == false)
596*4882a593Smuzhiyun {
597*4882a593Smuzhiyun DBG( "Invalid device!\n" );
598*4882a593Smuzhiyun return;
599*4882a593Smuzhiyun }
600*4882a593Smuzhiyun
601*4882a593Smuzhiyun #ifdef READ_QMI_URB_ERROR
602*4882a593Smuzhiyun del_timer(&pDev->mQMIDev.mReadUrbTimer);
603*4882a593Smuzhiyun if ((pReadURB->status == -ECONNRESET) && (pReadURB->actual_length > 0))
604*4882a593Smuzhiyun pReadURB->status = 0;
605*4882a593Smuzhiyun #endif
606*4882a593Smuzhiyun
607*4882a593Smuzhiyun if (pReadURB->status != 0)
608*4882a593Smuzhiyun {
609*4882a593Smuzhiyun DBG( "Read status = %d\n", pReadURB->status );
610*4882a593Smuzhiyun
611*4882a593Smuzhiyun // Resubmit the interrupt URB
612*4882a593Smuzhiyun ResubmitIntURB( pDev->mQMIDev.mpIntURB );
613*4882a593Smuzhiyun
614*4882a593Smuzhiyun return;
615*4882a593Smuzhiyun }
616*4882a593Smuzhiyun DBG( "Read %d bytes\n", pReadURB->actual_length );
617*4882a593Smuzhiyun
618*4882a593Smuzhiyun #ifdef QUECTEL_QMI_MERGE
619*4882a593Smuzhiyun if(MergeRecQmiMsg(&pDev->mQMIDev, pReadURB))
620*4882a593Smuzhiyun {
621*4882a593Smuzhiyun DBG( "not a full packet, read again\n");
622*4882a593Smuzhiyun // Resubmit the interrupt URB
623*4882a593Smuzhiyun ResubmitIntURB( pDev->mQMIDev.mpIntURB );
624*4882a593Smuzhiyun return;
625*4882a593Smuzhiyun }
626*4882a593Smuzhiyun #endif
627*4882a593Smuzhiyun
628*4882a593Smuzhiyun pData = pReadURB->transfer_buffer;
629*4882a593Smuzhiyun dataSize = pReadURB->actual_length;
630*4882a593Smuzhiyun
631*4882a593Smuzhiyun PrintHex( pData, dataSize );
632*4882a593Smuzhiyun
633*4882a593Smuzhiyun #ifdef READ_QMI_URB_ERROR
634*4882a593Smuzhiyun if (dataSize < (le16_to_cpu(get_unaligned((u16*)(pData + 1))) + 1)) {
635*4882a593Smuzhiyun dataSize = (le16_to_cpu(get_unaligned((u16*)(pData + 1))) + 1);
636*4882a593Smuzhiyun memset(pReadURB->transfer_buffer + pReadURB->actual_length, 0x00, dataSize - pReadURB->actual_length);
637*4882a593Smuzhiyun INFO( "Read %d / %d bytes\n", pReadURB->actual_length, dataSize);
638*4882a593Smuzhiyun }
639*4882a593Smuzhiyun #endif
640*4882a593Smuzhiyun
641*4882a593Smuzhiyun result = ParseQMUX( &clientID,
642*4882a593Smuzhiyun pData,
643*4882a593Smuzhiyun dataSize );
644*4882a593Smuzhiyun if (result < 0)
645*4882a593Smuzhiyun {
646*4882a593Smuzhiyun DBG( "Read error parsing QMUX %d\n", result );
647*4882a593Smuzhiyun
648*4882a593Smuzhiyun // Resubmit the interrupt URB
649*4882a593Smuzhiyun ResubmitIntURB( pDev->mQMIDev.mpIntURB );
650*4882a593Smuzhiyun
651*4882a593Smuzhiyun return;
652*4882a593Smuzhiyun }
653*4882a593Smuzhiyun
654*4882a593Smuzhiyun // Grab transaction ID
655*4882a593Smuzhiyun
656*4882a593Smuzhiyun // Data large enough?
657*4882a593Smuzhiyun if (dataSize < result + 3)
658*4882a593Smuzhiyun {
659*4882a593Smuzhiyun DBG( "Data buffer too small to parse\n" );
660*4882a593Smuzhiyun
661*4882a593Smuzhiyun // Resubmit the interrupt URB
662*4882a593Smuzhiyun ResubmitIntURB( pDev->mQMIDev.mpIntURB );
663*4882a593Smuzhiyun
664*4882a593Smuzhiyun return;
665*4882a593Smuzhiyun }
666*4882a593Smuzhiyun
667*4882a593Smuzhiyun // Transaction ID size is 1 for QMICTL, 2 for others
668*4882a593Smuzhiyun if (clientID == QMICTL)
669*4882a593Smuzhiyun {
670*4882a593Smuzhiyun transactionID = *(u8*)(pData + result + 1);
671*4882a593Smuzhiyun }
672*4882a593Smuzhiyun else
673*4882a593Smuzhiyun {
674*4882a593Smuzhiyun transactionID = le16_to_cpu( get_unaligned((u16*)(pData + result + 1)) );
675*4882a593Smuzhiyun }
676*4882a593Smuzhiyun
677*4882a593Smuzhiyun // Critical section
678*4882a593Smuzhiyun spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
679*4882a593Smuzhiyun
680*4882a593Smuzhiyun // Find memory storage for this service and Client ID
681*4882a593Smuzhiyun // Not using FindClientMem because it can't handle broadcasts
682*4882a593Smuzhiyun pClientMem = pDev->mQMIDev.mpClientMemList;
683*4882a593Smuzhiyun
684*4882a593Smuzhiyun while (pClientMem != NULL)
685*4882a593Smuzhiyun {
686*4882a593Smuzhiyun if (pClientMem->mClientID == clientID
687*4882a593Smuzhiyun || (pClientMem->mClientID | 0xff00) == clientID)
688*4882a593Smuzhiyun {
689*4882a593Smuzhiyun // Make copy of pData
690*4882a593Smuzhiyun pDataCopy = kmalloc( dataSize, GFP_ATOMIC );
691*4882a593Smuzhiyun if (pDataCopy == NULL)
692*4882a593Smuzhiyun {
693*4882a593Smuzhiyun DBG( "Error allocating client data memory\n" );
694*4882a593Smuzhiyun
695*4882a593Smuzhiyun // End critical section
696*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
697*4882a593Smuzhiyun
698*4882a593Smuzhiyun // Resubmit the interrupt URB
699*4882a593Smuzhiyun ResubmitIntURB( pDev->mQMIDev.mpIntURB );
700*4882a593Smuzhiyun
701*4882a593Smuzhiyun return;
702*4882a593Smuzhiyun }
703*4882a593Smuzhiyun
704*4882a593Smuzhiyun memcpy( pDataCopy, pData, dataSize );
705*4882a593Smuzhiyun
706*4882a593Smuzhiyun if (AddToReadMemList( pDev,
707*4882a593Smuzhiyun pClientMem->mClientID,
708*4882a593Smuzhiyun transactionID,
709*4882a593Smuzhiyun pDataCopy,
710*4882a593Smuzhiyun dataSize ) == false)
711*4882a593Smuzhiyun {
712*4882a593Smuzhiyun DBG( "Error allocating pReadMemListEntry "
713*4882a593Smuzhiyun "read will be discarded\n" );
714*4882a593Smuzhiyun kfree( pDataCopy );
715*4882a593Smuzhiyun
716*4882a593Smuzhiyun // End critical section
717*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
718*4882a593Smuzhiyun
719*4882a593Smuzhiyun // Resubmit the interrupt URB
720*4882a593Smuzhiyun ResubmitIntURB( pDev->mQMIDev.mpIntURB );
721*4882a593Smuzhiyun
722*4882a593Smuzhiyun return;
723*4882a593Smuzhiyun }
724*4882a593Smuzhiyun
725*4882a593Smuzhiyun // Success
726*4882a593Smuzhiyun VDBG( "Creating new readListEntry for client 0x%04X, TID %x\n",
727*4882a593Smuzhiyun clientID,
728*4882a593Smuzhiyun transactionID );
729*4882a593Smuzhiyun
730*4882a593Smuzhiyun // Notify this client data exists
731*4882a593Smuzhiyun NotifyAndPopNotifyList( pDev,
732*4882a593Smuzhiyun pClientMem->mClientID,
733*4882a593Smuzhiyun transactionID );
734*4882a593Smuzhiyun
735*4882a593Smuzhiyun // Possibly notify poll() that data exists
736*4882a593Smuzhiyun wake_up_interruptible_sync( &pClientMem->mWaitQueue );
737*4882a593Smuzhiyun
738*4882a593Smuzhiyun // Not a broadcast
739*4882a593Smuzhiyun if (clientID >> 8 != 0xff)
740*4882a593Smuzhiyun {
741*4882a593Smuzhiyun break;
742*4882a593Smuzhiyun }
743*4882a593Smuzhiyun }
744*4882a593Smuzhiyun
745*4882a593Smuzhiyun // Next element
746*4882a593Smuzhiyun pClientMem = pClientMem->mpNext;
747*4882a593Smuzhiyun }
748*4882a593Smuzhiyun
749*4882a593Smuzhiyun // End critical section
750*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
751*4882a593Smuzhiyun
752*4882a593Smuzhiyun // Resubmit the interrupt URB
753*4882a593Smuzhiyun ResubmitIntURB( pDev->mQMIDev.mpIntURB );
754*4882a593Smuzhiyun }
755*4882a593Smuzhiyun
756*4882a593Smuzhiyun /*===========================================================================
757*4882a593Smuzhiyun METHOD:
758*4882a593Smuzhiyun IntCallback (Public Method)
759*4882a593Smuzhiyun
760*4882a593Smuzhiyun DESCRIPTION:
761*4882a593Smuzhiyun Data is available, fire off a read URB
762*4882a593Smuzhiyun
763*4882a593Smuzhiyun PARAMETERS
764*4882a593Smuzhiyun pIntURB [ I ] - URB this callback is run for
765*4882a593Smuzhiyun
766*4882a593Smuzhiyun RETURN VALUE:
767*4882a593Smuzhiyun None
768*4882a593Smuzhiyun ===========================================================================*/
769*4882a593Smuzhiyun #if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,18 ))
IntCallback(struct urb * pIntURB)770*4882a593Smuzhiyun static void IntCallback( struct urb * pIntURB )
771*4882a593Smuzhiyun {
772*4882a593Smuzhiyun #else
773*4882a593Smuzhiyun static void IntCallback(struct urb *pIntURB, struct pt_regs *regs)
774*4882a593Smuzhiyun {
775*4882a593Smuzhiyun #endif
776*4882a593Smuzhiyun int status;
777*4882a593Smuzhiyun struct usb_cdc_notification *dr;
778*4882a593Smuzhiyun
779*4882a593Smuzhiyun sGobiUSBNet * pDev = (sGobiUSBNet *)pIntURB->context;
780*4882a593Smuzhiyun dr = (struct usb_cdc_notification *)pDev->mQMIDev.mpIntBuffer;
781*4882a593Smuzhiyun
782*4882a593Smuzhiyun if (IsDeviceValid( pDev ) == false)
783*4882a593Smuzhiyun {
784*4882a593Smuzhiyun DBG( "Invalid device!\n" );
785*4882a593Smuzhiyun return;
786*4882a593Smuzhiyun }
787*4882a593Smuzhiyun
788*4882a593Smuzhiyun // Verify this was a normal interrupt
789*4882a593Smuzhiyun if (pIntURB->status != 0)
790*4882a593Smuzhiyun {
791*4882a593Smuzhiyun DBG( "IntCallback: Int status = %d\n", pIntURB->status );
792*4882a593Smuzhiyun
793*4882a593Smuzhiyun // Ignore EOVERFLOW errors
794*4882a593Smuzhiyun if (pIntURB->status != -EOVERFLOW)
795*4882a593Smuzhiyun {
796*4882a593Smuzhiyun // Read 'thread' dies here
797*4882a593Smuzhiyun return;
798*4882a593Smuzhiyun }
799*4882a593Smuzhiyun }
800*4882a593Smuzhiyun else
801*4882a593Smuzhiyun {
802*4882a593Smuzhiyun //TODO cast transfer_buffer to struct usb_cdc_notification
803*4882a593Smuzhiyun
804*4882a593Smuzhiyun VDBG( "IntCallback: Encapsulated Response = 0x%llx\n",
805*4882a593Smuzhiyun (*(u64*)pIntURB->transfer_buffer));
806*4882a593Smuzhiyun
807*4882a593Smuzhiyun switch (dr->bNotificationType) {
808*4882a593Smuzhiyun case USB_CDC_NOTIFY_RESPONSE_AVAILABLE: //0x01
809*4882a593Smuzhiyun {
810*4882a593Smuzhiyun // Time to read
811*4882a593Smuzhiyun usb_fill_control_urb( pDev->mQMIDev.mpReadURB,
812*4882a593Smuzhiyun pDev->mpNetDev->udev,
813*4882a593Smuzhiyun usb_rcvctrlpipe( pDev->mpNetDev->udev, 0 ),
814*4882a593Smuzhiyun (unsigned char *)pDev->mQMIDev.mpReadSetupPacket,
815*4882a593Smuzhiyun pDev->mQMIDev.mpReadBuffer,
816*4882a593Smuzhiyun DEFAULT_READ_URB_LENGTH,
817*4882a593Smuzhiyun ReadCallback,
818*4882a593Smuzhiyun pDev );
819*4882a593Smuzhiyun #ifdef READ_QMI_URB_ERROR
820*4882a593Smuzhiyun mod_timer( &pDev->mQMIDev.mReadUrbTimer, jiffies + msecs_to_jiffies(300) );
821*4882a593Smuzhiyun #endif
822*4882a593Smuzhiyun status = usb_submit_urb( pDev->mQMIDev.mpReadURB, GFP_ATOMIC );
823*4882a593Smuzhiyun if (status != 0)
824*4882a593Smuzhiyun {
825*4882a593Smuzhiyun DBG("Error submitting Read URB %d\n", status);
826*4882a593Smuzhiyun // Resubmit the interrupt urb
827*4882a593Smuzhiyun ResubmitIntURB(pIntURB);
828*4882a593Smuzhiyun return;
829*4882a593Smuzhiyun }
830*4882a593Smuzhiyun
831*4882a593Smuzhiyun // Int URB will be resubmitted during ReadCallback
832*4882a593Smuzhiyun return;
833*4882a593Smuzhiyun }
834*4882a593Smuzhiyun case USB_CDC_NOTIFY_SPEED_CHANGE: //0x2a
835*4882a593Smuzhiyun {
836*4882a593Smuzhiyun DBG( "IntCallback: Connection Speed Change = 0x%llx\n",
837*4882a593Smuzhiyun (*(u64*)pIntURB->transfer_buffer));
838*4882a593Smuzhiyun
839*4882a593Smuzhiyun // if upstream or downstream is 0, stop traffic. Otherwise resume it
840*4882a593Smuzhiyun if ((*(u32*)(pIntURB->transfer_buffer + 8) == 0)
841*4882a593Smuzhiyun || (*(u32*)(pIntURB->transfer_buffer + 12) == 0))
842*4882a593Smuzhiyun {
843*4882a593Smuzhiyun GobiSetDownReason( pDev, CDC_CONNECTION_SPEED );
844*4882a593Smuzhiyun DBG( "traffic stopping due to CONNECTION_SPEED_CHANGE\n" );
845*4882a593Smuzhiyun }
846*4882a593Smuzhiyun else
847*4882a593Smuzhiyun {
848*4882a593Smuzhiyun GobiClearDownReason( pDev, CDC_CONNECTION_SPEED );
849*4882a593Smuzhiyun DBG( "resuming traffic due to CONNECTION_SPEED_CHANGE\n" );
850*4882a593Smuzhiyun }
851*4882a593Smuzhiyun }
852*4882a593Smuzhiyun break;
853*4882a593Smuzhiyun default:
854*4882a593Smuzhiyun {
855*4882a593Smuzhiyun DBG( "ignoring invalid interrupt in packet\n" );
856*4882a593Smuzhiyun PrintHex( pIntURB->transfer_buffer, pIntURB->actual_length );
857*4882a593Smuzhiyun }
858*4882a593Smuzhiyun }
859*4882a593Smuzhiyun
860*4882a593Smuzhiyun // Resubmit the interrupt urb
861*4882a593Smuzhiyun ResubmitIntURB( pIntURB );
862*4882a593Smuzhiyun
863*4882a593Smuzhiyun return;
864*4882a593Smuzhiyun }
865*4882a593Smuzhiyun }
866*4882a593Smuzhiyun
867*4882a593Smuzhiyun #ifdef READ_QMI_URB_ERROR
868*4882a593Smuzhiyun static void ReadUrbTimerFunc( struct urb * pReadURB )
869*4882a593Smuzhiyun {
870*4882a593Smuzhiyun int result;
871*4882a593Smuzhiyun
872*4882a593Smuzhiyun INFO( "%s called (%ld).\n", __func__, jiffies );
873*4882a593Smuzhiyun
874*4882a593Smuzhiyun if ((pReadURB != NULL) && (pReadURB->status == -EINPROGRESS))
875*4882a593Smuzhiyun {
876*4882a593Smuzhiyun // Asynchronously unlink URB. On success, -EINPROGRESS will be returned,
877*4882a593Smuzhiyun // URB status will be set to -ECONNRESET, and ReadCallback() executed
878*4882a593Smuzhiyun result = usb_unlink_urb( pReadURB );
879*4882a593Smuzhiyun INFO( "%s called usb_unlink_urb, result = %d\n", __func__, result);
880*4882a593Smuzhiyun }
881*4882a593Smuzhiyun }
882*4882a593Smuzhiyun #endif
883*4882a593Smuzhiyun
884*4882a593Smuzhiyun /*===========================================================================
885*4882a593Smuzhiyun METHOD:
886*4882a593Smuzhiyun StartRead (Public Method)
887*4882a593Smuzhiyun
888*4882a593Smuzhiyun DESCRIPTION:
889*4882a593Smuzhiyun Start continuous read "thread" (callback driven)
890*4882a593Smuzhiyun
891*4882a593Smuzhiyun Note: In case of error, KillRead() should be run
892*4882a593Smuzhiyun to remove urbs and clean up memory.
893*4882a593Smuzhiyun
894*4882a593Smuzhiyun PARAMETERS:
895*4882a593Smuzhiyun pDev [ I ] - Device specific memory
896*4882a593Smuzhiyun
897*4882a593Smuzhiyun RETURN VALUE:
898*4882a593Smuzhiyun int - 0 for success
899*4882a593Smuzhiyun negative errno for failure
900*4882a593Smuzhiyun ===========================================================================*/
901*4882a593Smuzhiyun int QuecStartRead( sGobiUSBNet * pDev )
902*4882a593Smuzhiyun {
903*4882a593Smuzhiyun int interval;
904*4882a593Smuzhiyun struct usb_endpoint_descriptor *pendp;
905*4882a593Smuzhiyun
906*4882a593Smuzhiyun if (IsDeviceValid( pDev ) == false)
907*4882a593Smuzhiyun {
908*4882a593Smuzhiyun DBG( "Invalid device!\n" );
909*4882a593Smuzhiyun return -ENXIO;
910*4882a593Smuzhiyun }
911*4882a593Smuzhiyun
912*4882a593Smuzhiyun // Allocate URB buffers
913*4882a593Smuzhiyun pDev->mQMIDev.mpReadURB = usb_alloc_urb( 0, GFP_KERNEL );
914*4882a593Smuzhiyun if (pDev->mQMIDev.mpReadURB == NULL)
915*4882a593Smuzhiyun {
916*4882a593Smuzhiyun DBG( "Error allocating read urb\n" );
917*4882a593Smuzhiyun return -ENOMEM;
918*4882a593Smuzhiyun }
919*4882a593Smuzhiyun
920*4882a593Smuzhiyun #ifdef READ_QMI_URB_ERROR
921*4882a593Smuzhiyun setup_timer( &pDev->mQMIDev.mReadUrbTimer, (void*)ReadUrbTimerFunc, (unsigned long)pDev->mQMIDev.mpReadURB );
922*4882a593Smuzhiyun #endif
923*4882a593Smuzhiyun
924*4882a593Smuzhiyun pDev->mQMIDev.mpIntURB = usb_alloc_urb( 0, GFP_KERNEL );
925*4882a593Smuzhiyun if (pDev->mQMIDev.mpIntURB == NULL)
926*4882a593Smuzhiyun {
927*4882a593Smuzhiyun DBG( "Error allocating int urb\n" );
928*4882a593Smuzhiyun usb_free_urb( pDev->mQMIDev.mpReadURB );
929*4882a593Smuzhiyun pDev->mQMIDev.mpReadURB = NULL;
930*4882a593Smuzhiyun return -ENOMEM;
931*4882a593Smuzhiyun }
932*4882a593Smuzhiyun
933*4882a593Smuzhiyun // Create data buffers
934*4882a593Smuzhiyun pDev->mQMIDev.mpReadBuffer = kmalloc( DEFAULT_READ_URB_LENGTH, GFP_KERNEL );
935*4882a593Smuzhiyun if (pDev->mQMIDev.mpReadBuffer == NULL)
936*4882a593Smuzhiyun {
937*4882a593Smuzhiyun DBG( "Error allocating read buffer\n" );
938*4882a593Smuzhiyun usb_free_urb( pDev->mQMIDev.mpIntURB );
939*4882a593Smuzhiyun pDev->mQMIDev.mpIntURB = NULL;
940*4882a593Smuzhiyun usb_free_urb( pDev->mQMIDev.mpReadURB );
941*4882a593Smuzhiyun pDev->mQMIDev.mpReadURB = NULL;
942*4882a593Smuzhiyun return -ENOMEM;
943*4882a593Smuzhiyun }
944*4882a593Smuzhiyun
945*4882a593Smuzhiyun pDev->mQMIDev.mpIntBuffer = kmalloc( 64, GFP_KERNEL );
946*4882a593Smuzhiyun if (pDev->mQMIDev.mpIntBuffer == NULL)
947*4882a593Smuzhiyun {
948*4882a593Smuzhiyun DBG( "Error allocating int buffer\n" );
949*4882a593Smuzhiyun kfree( pDev->mQMIDev.mpReadBuffer );
950*4882a593Smuzhiyun pDev->mQMIDev.mpReadBuffer = NULL;
951*4882a593Smuzhiyun usb_free_urb( pDev->mQMIDev.mpIntURB );
952*4882a593Smuzhiyun pDev->mQMIDev.mpIntURB = NULL;
953*4882a593Smuzhiyun usb_free_urb( pDev->mQMIDev.mpReadURB );
954*4882a593Smuzhiyun pDev->mQMIDev.mpReadURB = NULL;
955*4882a593Smuzhiyun return -ENOMEM;
956*4882a593Smuzhiyun }
957*4882a593Smuzhiyun
958*4882a593Smuzhiyun pDev->mQMIDev.mpReadSetupPacket = kmalloc( sizeof( sURBSetupPacket ),
959*4882a593Smuzhiyun GFP_KERNEL );
960*4882a593Smuzhiyun if (pDev->mQMIDev.mpReadSetupPacket == NULL)
961*4882a593Smuzhiyun {
962*4882a593Smuzhiyun DBG( "Error allocating setup packet buffer\n" );
963*4882a593Smuzhiyun kfree( pDev->mQMIDev.mpIntBuffer );
964*4882a593Smuzhiyun pDev->mQMIDev.mpIntBuffer = NULL;
965*4882a593Smuzhiyun kfree( pDev->mQMIDev.mpReadBuffer );
966*4882a593Smuzhiyun pDev->mQMIDev.mpReadBuffer = NULL;
967*4882a593Smuzhiyun usb_free_urb( pDev->mQMIDev.mpIntURB );
968*4882a593Smuzhiyun pDev->mQMIDev.mpIntURB = NULL;
969*4882a593Smuzhiyun usb_free_urb( pDev->mQMIDev.mpReadURB );
970*4882a593Smuzhiyun pDev->mQMIDev.mpReadURB = NULL;
971*4882a593Smuzhiyun return -ENOMEM;
972*4882a593Smuzhiyun }
973*4882a593Smuzhiyun
974*4882a593Smuzhiyun // CDC Get Encapsulated Response packet
975*4882a593Smuzhiyun pDev->mQMIDev.mpReadSetupPacket->mRequestType = 0xA1;
976*4882a593Smuzhiyun pDev->mQMIDev.mpReadSetupPacket->mRequestCode = 1;
977*4882a593Smuzhiyun pDev->mQMIDev.mpReadSetupPacket->mValue = 0;
978*4882a593Smuzhiyun pDev->mQMIDev.mpReadSetupPacket->mIndex =
979*4882a593Smuzhiyun cpu_to_le16(pDev->mpIntf->cur_altsetting->desc.bInterfaceNumber); /* interface number */
980*4882a593Smuzhiyun pDev->mQMIDev.mpReadSetupPacket->mLength = cpu_to_le16(DEFAULT_READ_URB_LENGTH);
981*4882a593Smuzhiyun
982*4882a593Smuzhiyun pendp = GetEndpoint(pDev->mpIntf, USB_ENDPOINT_XFER_INT, USB_DIR_IN);
983*4882a593Smuzhiyun if (pendp == NULL)
984*4882a593Smuzhiyun {
985*4882a593Smuzhiyun DBG( "Invalid interrupt endpoint!\n" );
986*4882a593Smuzhiyun kfree(pDev->mQMIDev.mpReadSetupPacket);
987*4882a593Smuzhiyun pDev->mQMIDev.mpReadSetupPacket = NULL;
988*4882a593Smuzhiyun kfree( pDev->mQMIDev.mpIntBuffer );
989*4882a593Smuzhiyun pDev->mQMIDev.mpIntBuffer = NULL;
990*4882a593Smuzhiyun kfree( pDev->mQMIDev.mpReadBuffer );
991*4882a593Smuzhiyun pDev->mQMIDev.mpReadBuffer = NULL;
992*4882a593Smuzhiyun usb_free_urb( pDev->mQMIDev.mpIntURB );
993*4882a593Smuzhiyun pDev->mQMIDev.mpIntURB = NULL;
994*4882a593Smuzhiyun usb_free_urb( pDev->mQMIDev.mpReadURB );
995*4882a593Smuzhiyun pDev->mQMIDev.mpReadURB = NULL;
996*4882a593Smuzhiyun return -ENXIO;
997*4882a593Smuzhiyun }
998*4882a593Smuzhiyun
999*4882a593Smuzhiyun #ifdef QUECTEL_QMI_MERGE
1000*4882a593Smuzhiyun pDev->mQMIDev.mpQmiMsgPacket = kmalloc( sizeof(sQMIMsgPacket), GFP_KERNEL );
1001*4882a593Smuzhiyun if (pDev->mQMIDev.mpQmiMsgPacket == NULL)
1002*4882a593Smuzhiyun {
1003*4882a593Smuzhiyun DBG( "Error allocating qmi msg merge packet buffer!\n" );
1004*4882a593Smuzhiyun kfree(pDev->mQMIDev.mpReadSetupPacket);
1005*4882a593Smuzhiyun pDev->mQMIDev.mpReadSetupPacket = NULL;
1006*4882a593Smuzhiyun kfree( pDev->mQMIDev.mpIntBuffer );
1007*4882a593Smuzhiyun pDev->mQMIDev.mpIntBuffer = NULL;
1008*4882a593Smuzhiyun kfree( pDev->mQMIDev.mpReadBuffer );
1009*4882a593Smuzhiyun pDev->mQMIDev.mpReadBuffer = NULL;
1010*4882a593Smuzhiyun usb_free_urb( pDev->mQMIDev.mpIntURB );
1011*4882a593Smuzhiyun pDev->mQMIDev.mpIntURB = NULL;
1012*4882a593Smuzhiyun usb_free_urb( pDev->mQMIDev.mpReadURB );
1013*4882a593Smuzhiyun pDev->mQMIDev.mpReadURB = NULL;
1014*4882a593Smuzhiyun return -ENOMEM;
1015*4882a593Smuzhiyun }
1016*4882a593Smuzhiyun #endif
1017*4882a593Smuzhiyun
1018*4882a593Smuzhiyun // Interval needs reset after every URB completion
1019*4882a593Smuzhiyun interval = max((int)(pendp->bInterval),
1020*4882a593Smuzhiyun (pDev->mpNetDev->udev->speed == USB_SPEED_HIGH) ? 7 : 3);
1021*4882a593Smuzhiyun #if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,22 ))
1022*4882a593Smuzhiyun s_interval = interval;
1023*4882a593Smuzhiyun #endif
1024*4882a593Smuzhiyun
1025*4882a593Smuzhiyun // Schedule interrupt URB
1026*4882a593Smuzhiyun usb_fill_int_urb( pDev->mQMIDev.mpIntURB,
1027*4882a593Smuzhiyun pDev->mpNetDev->udev,
1028*4882a593Smuzhiyun /* QMI interrupt endpoint for the following
1029*4882a593Smuzhiyun * interface configuration: DM, NMEA, MDM, NET
1030*4882a593Smuzhiyun */
1031*4882a593Smuzhiyun usb_rcvintpipe( pDev->mpNetDev->udev,
1032*4882a593Smuzhiyun pendp->bEndpointAddress),
1033*4882a593Smuzhiyun pDev->mQMIDev.mpIntBuffer,
1034*4882a593Smuzhiyun min((int)le16_to_cpu(pendp->wMaxPacketSize), 64),
1035*4882a593Smuzhiyun IntCallback,
1036*4882a593Smuzhiyun pDev,
1037*4882a593Smuzhiyun interval );
1038*4882a593Smuzhiyun return usb_submit_urb( pDev->mQMIDev.mpIntURB, GFP_KERNEL );
1039*4882a593Smuzhiyun }
1040*4882a593Smuzhiyun
1041*4882a593Smuzhiyun /*===========================================================================
1042*4882a593Smuzhiyun METHOD:
1043*4882a593Smuzhiyun KillRead (Public Method)
1044*4882a593Smuzhiyun
1045*4882a593Smuzhiyun DESCRIPTION:
1046*4882a593Smuzhiyun Kill continuous read "thread"
1047*4882a593Smuzhiyun
1048*4882a593Smuzhiyun PARAMETERS:
1049*4882a593Smuzhiyun pDev [ I ] - Device specific memory
1050*4882a593Smuzhiyun
1051*4882a593Smuzhiyun RETURN VALUE:
1052*4882a593Smuzhiyun None
1053*4882a593Smuzhiyun ===========================================================================*/
1054*4882a593Smuzhiyun void QuecKillRead( sGobiUSBNet * pDev )
1055*4882a593Smuzhiyun {
1056*4882a593Smuzhiyun // Stop reading
1057*4882a593Smuzhiyun if (pDev->mQMIDev.mpReadURB != NULL)
1058*4882a593Smuzhiyun {
1059*4882a593Smuzhiyun DBG( "Killng read URB\n" );
1060*4882a593Smuzhiyun usb_kill_urb( pDev->mQMIDev.mpReadURB );
1061*4882a593Smuzhiyun }
1062*4882a593Smuzhiyun
1063*4882a593Smuzhiyun if (pDev->mQMIDev.mpIntURB != NULL)
1064*4882a593Smuzhiyun {
1065*4882a593Smuzhiyun DBG( "Killng int URB\n" );
1066*4882a593Smuzhiyun usb_kill_urb( pDev->mQMIDev.mpIntURB );
1067*4882a593Smuzhiyun }
1068*4882a593Smuzhiyun
1069*4882a593Smuzhiyun // Release buffers
1070*4882a593Smuzhiyun kfree( pDev->mQMIDev.mpReadSetupPacket );
1071*4882a593Smuzhiyun pDev->mQMIDev.mpReadSetupPacket = NULL;
1072*4882a593Smuzhiyun kfree( pDev->mQMIDev.mpReadBuffer );
1073*4882a593Smuzhiyun pDev->mQMIDev.mpReadBuffer = NULL;
1074*4882a593Smuzhiyun kfree( pDev->mQMIDev.mpIntBuffer );
1075*4882a593Smuzhiyun pDev->mQMIDev.mpIntBuffer = NULL;
1076*4882a593Smuzhiyun
1077*4882a593Smuzhiyun // Release URB's
1078*4882a593Smuzhiyun usb_free_urb( pDev->mQMIDev.mpReadURB );
1079*4882a593Smuzhiyun pDev->mQMIDev.mpReadURB = NULL;
1080*4882a593Smuzhiyun usb_free_urb( pDev->mQMIDev.mpIntURB );
1081*4882a593Smuzhiyun pDev->mQMIDev.mpIntURB = NULL;
1082*4882a593Smuzhiyun
1083*4882a593Smuzhiyun #ifdef QUECTEL_QMI_MERGE
1084*4882a593Smuzhiyun kfree( pDev->mQMIDev.mpQmiMsgPacket );
1085*4882a593Smuzhiyun pDev->mQMIDev.mpQmiMsgPacket = NULL;
1086*4882a593Smuzhiyun #endif
1087*4882a593Smuzhiyun }
1088*4882a593Smuzhiyun
1089*4882a593Smuzhiyun /*=========================================================================*/
1090*4882a593Smuzhiyun // Internal read/write functions
1091*4882a593Smuzhiyun /*=========================================================================*/
1092*4882a593Smuzhiyun
1093*4882a593Smuzhiyun /*===========================================================================
1094*4882a593Smuzhiyun METHOD:
1095*4882a593Smuzhiyun ReadAsync (Public Method)
1096*4882a593Smuzhiyun
1097*4882a593Smuzhiyun DESCRIPTION:
1098*4882a593Smuzhiyun Start asynchronous read
1099*4882a593Smuzhiyun NOTE: Reading client's data store, not device
1100*4882a593Smuzhiyun
1101*4882a593Smuzhiyun PARAMETERS:
1102*4882a593Smuzhiyun pDev [ I ] - Device specific memory
1103*4882a593Smuzhiyun clientID [ I ] - Requester's client ID
1104*4882a593Smuzhiyun transactionID [ I ] - Transaction ID or 0 for any
1105*4882a593Smuzhiyun pCallback [ I ] - Callback to be executed when data is available
1106*4882a593Smuzhiyun pData [ I ] - Data buffer that willl be passed (unmodified)
1107*4882a593Smuzhiyun to callback
1108*4882a593Smuzhiyun
1109*4882a593Smuzhiyun RETURN VALUE:
1110*4882a593Smuzhiyun int - 0 for success
1111*4882a593Smuzhiyun negative errno for failure
1112*4882a593Smuzhiyun ===========================================================================*/
1113*4882a593Smuzhiyun static int ReadAsync(
1114*4882a593Smuzhiyun sGobiUSBNet * pDev,
1115*4882a593Smuzhiyun u16 clientID,
1116*4882a593Smuzhiyun u16 transactionID,
1117*4882a593Smuzhiyun void (*pCallback)(sGobiUSBNet*, u16, void *),
1118*4882a593Smuzhiyun void * pData )
1119*4882a593Smuzhiyun {
1120*4882a593Smuzhiyun sClientMemList * pClientMem;
1121*4882a593Smuzhiyun sReadMemList ** ppReadMemList;
1122*4882a593Smuzhiyun
1123*4882a593Smuzhiyun unsigned long flags;
1124*4882a593Smuzhiyun
1125*4882a593Smuzhiyun if (IsDeviceValid( pDev ) == false)
1126*4882a593Smuzhiyun {
1127*4882a593Smuzhiyun DBG( "Invalid device!\n" );
1128*4882a593Smuzhiyun return -ENXIO;
1129*4882a593Smuzhiyun }
1130*4882a593Smuzhiyun
1131*4882a593Smuzhiyun // Critical section
1132*4882a593Smuzhiyun spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
1133*4882a593Smuzhiyun
1134*4882a593Smuzhiyun // Find memory storage for this client ID
1135*4882a593Smuzhiyun pClientMem = FindClientMem( pDev, clientID );
1136*4882a593Smuzhiyun if (pClientMem == NULL)
1137*4882a593Smuzhiyun {
1138*4882a593Smuzhiyun DBG( "Could not find matching client ID 0x%04X\n",
1139*4882a593Smuzhiyun clientID );
1140*4882a593Smuzhiyun
1141*4882a593Smuzhiyun // End critical section
1142*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
1143*4882a593Smuzhiyun return -ENXIO;
1144*4882a593Smuzhiyun }
1145*4882a593Smuzhiyun
1146*4882a593Smuzhiyun ppReadMemList = &(pClientMem->mpList);
1147*4882a593Smuzhiyun
1148*4882a593Smuzhiyun // Does data already exist?
1149*4882a593Smuzhiyun while (*ppReadMemList != NULL)
1150*4882a593Smuzhiyun {
1151*4882a593Smuzhiyun // Is this element our data?
1152*4882a593Smuzhiyun if (transactionID == 0
1153*4882a593Smuzhiyun || transactionID == (*ppReadMemList)->mTransactionID)
1154*4882a593Smuzhiyun {
1155*4882a593Smuzhiyun // End critical section
1156*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
1157*4882a593Smuzhiyun
1158*4882a593Smuzhiyun // Run our own callback
1159*4882a593Smuzhiyun pCallback( pDev, clientID, pData );
1160*4882a593Smuzhiyun
1161*4882a593Smuzhiyun return 0;
1162*4882a593Smuzhiyun }
1163*4882a593Smuzhiyun
1164*4882a593Smuzhiyun // Next
1165*4882a593Smuzhiyun ppReadMemList = &(*ppReadMemList)->mpNext;
1166*4882a593Smuzhiyun }
1167*4882a593Smuzhiyun
1168*4882a593Smuzhiyun // Data not found, add ourself to list of waiters
1169*4882a593Smuzhiyun if (AddToNotifyList( pDev,
1170*4882a593Smuzhiyun clientID,
1171*4882a593Smuzhiyun transactionID,
1172*4882a593Smuzhiyun pCallback,
1173*4882a593Smuzhiyun pData ) == false)
1174*4882a593Smuzhiyun {
1175*4882a593Smuzhiyun DBG( "Unable to register for notification\n" );
1176*4882a593Smuzhiyun }
1177*4882a593Smuzhiyun
1178*4882a593Smuzhiyun // End critical section
1179*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
1180*4882a593Smuzhiyun
1181*4882a593Smuzhiyun // Success
1182*4882a593Smuzhiyun return 0;
1183*4882a593Smuzhiyun }
1184*4882a593Smuzhiyun
1185*4882a593Smuzhiyun /*===========================================================================
1186*4882a593Smuzhiyun METHOD:
1187*4882a593Smuzhiyun UpSem (Public Method)
1188*4882a593Smuzhiyun
1189*4882a593Smuzhiyun DESCRIPTION:
1190*4882a593Smuzhiyun Notification function for synchronous read
1191*4882a593Smuzhiyun
1192*4882a593Smuzhiyun PARAMETERS:
1193*4882a593Smuzhiyun pDev [ I ] - Device specific memory
1194*4882a593Smuzhiyun clientID [ I ] - Requester's client ID
1195*4882a593Smuzhiyun pData [ I ] - Buffer that holds semaphore to be up()-ed
1196*4882a593Smuzhiyun
1197*4882a593Smuzhiyun RETURN VALUE:
1198*4882a593Smuzhiyun None
1199*4882a593Smuzhiyun ===========================================================================*/
1200*4882a593Smuzhiyun #define QUEC_SEM_MAGIC 0x12345678
1201*4882a593Smuzhiyun struct QuecSem {
1202*4882a593Smuzhiyun struct semaphore readSem;
1203*4882a593Smuzhiyun int magic;
1204*4882a593Smuzhiyun };
1205*4882a593Smuzhiyun
1206*4882a593Smuzhiyun static void UpSem(
1207*4882a593Smuzhiyun sGobiUSBNet * pDev,
1208*4882a593Smuzhiyun u16 clientID,
1209*4882a593Smuzhiyun void * pData )
1210*4882a593Smuzhiyun {
1211*4882a593Smuzhiyun struct QuecSem *pSem = (struct QuecSem *)pData;
1212*4882a593Smuzhiyun
1213*4882a593Smuzhiyun VDBG( "0x%04X\n", clientID );
1214*4882a593Smuzhiyun
1215*4882a593Smuzhiyun if (pSem->magic == QUEC_SEM_MAGIC)
1216*4882a593Smuzhiyun up( &(pSem->readSem) );
1217*4882a593Smuzhiyun else
1218*4882a593Smuzhiyun kfree(pSem);
1219*4882a593Smuzhiyun return;
1220*4882a593Smuzhiyun }
1221*4882a593Smuzhiyun
1222*4882a593Smuzhiyun /*===========================================================================
1223*4882a593Smuzhiyun METHOD:
1224*4882a593Smuzhiyun ReadSync (Public Method)
1225*4882a593Smuzhiyun
1226*4882a593Smuzhiyun DESCRIPTION:
1227*4882a593Smuzhiyun Start synchronous read
1228*4882a593Smuzhiyun NOTE: Reading client's data store, not device
1229*4882a593Smuzhiyun
1230*4882a593Smuzhiyun PARAMETERS:
1231*4882a593Smuzhiyun pDev [ I ] - Device specific memory
1232*4882a593Smuzhiyun ppOutBuffer [I/O] - On success, will be filled with a
1233*4882a593Smuzhiyun pointer to read buffer
1234*4882a593Smuzhiyun clientID [ I ] - Requester's client ID
1235*4882a593Smuzhiyun transactionID [ I ] - Transaction ID or 0 for any
1236*4882a593Smuzhiyun
1237*4882a593Smuzhiyun RETURN VALUE:
1238*4882a593Smuzhiyun int - size of data read for success
1239*4882a593Smuzhiyun negative errno for failure
1240*4882a593Smuzhiyun ===========================================================================*/
1241*4882a593Smuzhiyun static int ReadSync(
1242*4882a593Smuzhiyun sGobiUSBNet * pDev,
1243*4882a593Smuzhiyun void ** ppOutBuffer,
1244*4882a593Smuzhiyun u16 clientID,
1245*4882a593Smuzhiyun u16 transactionID )
1246*4882a593Smuzhiyun {
1247*4882a593Smuzhiyun int result;
1248*4882a593Smuzhiyun sClientMemList * pClientMem;
1249*4882a593Smuzhiyun sNotifyList ** ppNotifyList, * pDelNotifyListEntry;
1250*4882a593Smuzhiyun struct QuecSem readSem;
1251*4882a593Smuzhiyun void * pData;
1252*4882a593Smuzhiyun unsigned long flags;
1253*4882a593Smuzhiyun u16 dataSize;
1254*4882a593Smuzhiyun
1255*4882a593Smuzhiyun if (IsDeviceValid( pDev ) == false)
1256*4882a593Smuzhiyun {
1257*4882a593Smuzhiyun DBG( "Invalid device!\n" );
1258*4882a593Smuzhiyun return -ENXIO;
1259*4882a593Smuzhiyun }
1260*4882a593Smuzhiyun
1261*4882a593Smuzhiyun // Critical section
1262*4882a593Smuzhiyun spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
1263*4882a593Smuzhiyun
1264*4882a593Smuzhiyun // Find memory storage for this Client ID
1265*4882a593Smuzhiyun pClientMem = FindClientMem( pDev, clientID );
1266*4882a593Smuzhiyun if (pClientMem == NULL)
1267*4882a593Smuzhiyun {
1268*4882a593Smuzhiyun DBG( "Could not find matching client ID 0x%04X\n",
1269*4882a593Smuzhiyun clientID );
1270*4882a593Smuzhiyun
1271*4882a593Smuzhiyun // End critical section
1272*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
1273*4882a593Smuzhiyun return -ENXIO;
1274*4882a593Smuzhiyun }
1275*4882a593Smuzhiyun
1276*4882a593Smuzhiyun // Note: in cases where read is interrupted,
1277*4882a593Smuzhiyun // this will verify client is still valid
1278*4882a593Smuzhiyun while (PopFromReadMemList( pDev,
1279*4882a593Smuzhiyun clientID,
1280*4882a593Smuzhiyun transactionID,
1281*4882a593Smuzhiyun &pData,
1282*4882a593Smuzhiyun &dataSize ) == false)
1283*4882a593Smuzhiyun {
1284*4882a593Smuzhiyun // Data does not yet exist, wait
1285*4882a593Smuzhiyun sema_init( &readSem.readSem, 0 );
1286*4882a593Smuzhiyun readSem.magic = QUEC_SEM_MAGIC;
1287*4882a593Smuzhiyun
1288*4882a593Smuzhiyun // Add ourself to list of waiters
1289*4882a593Smuzhiyun if (AddToNotifyList( pDev,
1290*4882a593Smuzhiyun clientID,
1291*4882a593Smuzhiyun transactionID,
1292*4882a593Smuzhiyun UpSem,
1293*4882a593Smuzhiyun &readSem ) == false)
1294*4882a593Smuzhiyun {
1295*4882a593Smuzhiyun DBG( "unable to register for notification\n" );
1296*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
1297*4882a593Smuzhiyun return -EFAULT;
1298*4882a593Smuzhiyun }
1299*4882a593Smuzhiyun
1300*4882a593Smuzhiyun // End critical section while we block
1301*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
1302*4882a593Smuzhiyun
1303*4882a593Smuzhiyun // Wait for notification
1304*4882a593Smuzhiyun result = down_interruptible( &readSem.readSem );
1305*4882a593Smuzhiyun //if (result) INFO("down_interruptible = %d\n", result);
1306*4882a593Smuzhiyun if (result == -EINTR) {
1307*4882a593Smuzhiyun result = down_timeout(&readSem.readSem, msecs_to_jiffies(200));
1308*4882a593Smuzhiyun //if (result) INFO("down_timeout = %d\n", result);
1309*4882a593Smuzhiyun }
1310*4882a593Smuzhiyun if (result != 0)
1311*4882a593Smuzhiyun {
1312*4882a593Smuzhiyun DBG( "Down Timeout %d\n", result );
1313*4882a593Smuzhiyun
1314*4882a593Smuzhiyun // readSem will fall out of scope,
1315*4882a593Smuzhiyun // remove from notify list so it's not referenced
1316*4882a593Smuzhiyun spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
1317*4882a593Smuzhiyun ppNotifyList = &(pClientMem->mpReadNotifyList);
1318*4882a593Smuzhiyun pDelNotifyListEntry = NULL;
1319*4882a593Smuzhiyun
1320*4882a593Smuzhiyun // Find and delete matching entry
1321*4882a593Smuzhiyun while (*ppNotifyList != NULL)
1322*4882a593Smuzhiyun {
1323*4882a593Smuzhiyun if ((*ppNotifyList)->mpData == &readSem)
1324*4882a593Smuzhiyun {
1325*4882a593Smuzhiyun pDelNotifyListEntry = *ppNotifyList;
1326*4882a593Smuzhiyun *ppNotifyList = (*ppNotifyList)->mpNext;
1327*4882a593Smuzhiyun kfree( pDelNotifyListEntry );
1328*4882a593Smuzhiyun break;
1329*4882a593Smuzhiyun }
1330*4882a593Smuzhiyun
1331*4882a593Smuzhiyun // Next
1332*4882a593Smuzhiyun ppNotifyList = &(*ppNotifyList)->mpNext;
1333*4882a593Smuzhiyun }
1334*4882a593Smuzhiyun
1335*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
1336*4882a593Smuzhiyun return -EINTR;
1337*4882a593Smuzhiyun }
1338*4882a593Smuzhiyun
1339*4882a593Smuzhiyun // Verify device is still valid
1340*4882a593Smuzhiyun if (IsDeviceValid( pDev ) == false)
1341*4882a593Smuzhiyun {
1342*4882a593Smuzhiyun DBG( "Invalid device!\n" );
1343*4882a593Smuzhiyun return -ENXIO;
1344*4882a593Smuzhiyun }
1345*4882a593Smuzhiyun
1346*4882a593Smuzhiyun // Restart critical section and continue loop
1347*4882a593Smuzhiyun spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
1348*4882a593Smuzhiyun }
1349*4882a593Smuzhiyun
1350*4882a593Smuzhiyun // End Critical section
1351*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
1352*4882a593Smuzhiyun
1353*4882a593Smuzhiyun // Success
1354*4882a593Smuzhiyun *ppOutBuffer = pData;
1355*4882a593Smuzhiyun
1356*4882a593Smuzhiyun return dataSize;
1357*4882a593Smuzhiyun }
1358*4882a593Smuzhiyun
1359*4882a593Smuzhiyun /*===========================================================================
1360*4882a593Smuzhiyun METHOD:
1361*4882a593Smuzhiyun WriteSyncCallback (Public Method)
1362*4882a593Smuzhiyun
1363*4882a593Smuzhiyun DESCRIPTION:
1364*4882a593Smuzhiyun Write callback
1365*4882a593Smuzhiyun
1366*4882a593Smuzhiyun PARAMETERS
1367*4882a593Smuzhiyun pWriteURB [ I ] - URB this callback is run for
1368*4882a593Smuzhiyun
1369*4882a593Smuzhiyun RETURN VALUE:
1370*4882a593Smuzhiyun None
1371*4882a593Smuzhiyun ===========================================================================*/
1372*4882a593Smuzhiyun #if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,18 ))
1373*4882a593Smuzhiyun static void WriteSyncCallback( struct urb * pWriteURB )
1374*4882a593Smuzhiyun #else
1375*4882a593Smuzhiyun static void WriteSyncCallback(struct urb *pWriteURB, struct pt_regs *regs)
1376*4882a593Smuzhiyun #endif
1377*4882a593Smuzhiyun {
1378*4882a593Smuzhiyun if (pWriteURB == NULL)
1379*4882a593Smuzhiyun {
1380*4882a593Smuzhiyun DBG( "null urb\n" );
1381*4882a593Smuzhiyun return;
1382*4882a593Smuzhiyun }
1383*4882a593Smuzhiyun
1384*4882a593Smuzhiyun DBG( "Write status/size %d/%d\n",
1385*4882a593Smuzhiyun pWriteURB->status,
1386*4882a593Smuzhiyun pWriteURB->actual_length );
1387*4882a593Smuzhiyun
1388*4882a593Smuzhiyun // Notify that write has completed by up()-ing semeaphore
1389*4882a593Smuzhiyun up( (struct semaphore * )pWriteURB->context );
1390*4882a593Smuzhiyun
1391*4882a593Smuzhiyun return;
1392*4882a593Smuzhiyun }
1393*4882a593Smuzhiyun
1394*4882a593Smuzhiyun /*===========================================================================
1395*4882a593Smuzhiyun METHOD:
1396*4882a593Smuzhiyun WriteSync (Public Method)
1397*4882a593Smuzhiyun
1398*4882a593Smuzhiyun DESCRIPTION:
1399*4882a593Smuzhiyun Start synchronous write
1400*4882a593Smuzhiyun
1401*4882a593Smuzhiyun PARAMETERS:
1402*4882a593Smuzhiyun pDev [ I ] - Device specific memory
1403*4882a593Smuzhiyun pWriteBuffer [ I ] - Data to be written
1404*4882a593Smuzhiyun writeBufferSize [ I ] - Size of data to be written
1405*4882a593Smuzhiyun clientID [ I ] - Client ID of requester
1406*4882a593Smuzhiyun
1407*4882a593Smuzhiyun RETURN VALUE:
1408*4882a593Smuzhiyun int - write size (includes QMUX)
1409*4882a593Smuzhiyun negative errno for failure
1410*4882a593Smuzhiyun ===========================================================================*/
1411*4882a593Smuzhiyun static int WriteSync(
1412*4882a593Smuzhiyun sGobiUSBNet * pDev,
1413*4882a593Smuzhiyun char * pWriteBuffer,
1414*4882a593Smuzhiyun int writeBufferSize,
1415*4882a593Smuzhiyun u16 clientID )
1416*4882a593Smuzhiyun {
1417*4882a593Smuzhiyun int result;
1418*4882a593Smuzhiyun struct semaphore writeSem;
1419*4882a593Smuzhiyun struct urb * pWriteURB;
1420*4882a593Smuzhiyun sURBSetupPacket *writeSetup;
1421*4882a593Smuzhiyun unsigned long flags;
1422*4882a593Smuzhiyun
1423*4882a593Smuzhiyun if (IsDeviceValid( pDev ) == false)
1424*4882a593Smuzhiyun {
1425*4882a593Smuzhiyun DBG( "Invalid device!\n" );
1426*4882a593Smuzhiyun return -ENXIO;
1427*4882a593Smuzhiyun }
1428*4882a593Smuzhiyun
1429*4882a593Smuzhiyun pWriteURB = usb_alloc_urb( 0, GFP_KERNEL );
1430*4882a593Smuzhiyun if (pWriteURB == NULL)
1431*4882a593Smuzhiyun {
1432*4882a593Smuzhiyun DBG( "URB mem error\n" );
1433*4882a593Smuzhiyun return -ENOMEM;
1434*4882a593Smuzhiyun }
1435*4882a593Smuzhiyun
1436*4882a593Smuzhiyun // Fill writeBuffer with QMUX
1437*4882a593Smuzhiyun result = FillQMUX( clientID, pWriteBuffer, writeBufferSize );
1438*4882a593Smuzhiyun if (result < 0)
1439*4882a593Smuzhiyun {
1440*4882a593Smuzhiyun usb_free_urb( pWriteURB );
1441*4882a593Smuzhiyun return result;
1442*4882a593Smuzhiyun }
1443*4882a593Smuzhiyun
1444*4882a593Smuzhiyun // CDC Send Encapsulated Request packet
1445*4882a593Smuzhiyun writeSetup = kmalloc(sizeof(sURBSetupPacket), GFP_KERNEL);
1446*4882a593Smuzhiyun writeSetup->mRequestType = 0x21;
1447*4882a593Smuzhiyun writeSetup->mRequestCode = 0;
1448*4882a593Smuzhiyun writeSetup->mValue = 0;
1449*4882a593Smuzhiyun writeSetup->mIndex = cpu_to_le16(pDev->mpIntf->cur_altsetting->desc.bInterfaceNumber);
1450*4882a593Smuzhiyun writeSetup->mLength = cpu_to_le16(writeBufferSize);
1451*4882a593Smuzhiyun
1452*4882a593Smuzhiyun // Create URB
1453*4882a593Smuzhiyun usb_fill_control_urb( pWriteURB,
1454*4882a593Smuzhiyun pDev->mpNetDev->udev,
1455*4882a593Smuzhiyun usb_sndctrlpipe( pDev->mpNetDev->udev, 0 ),
1456*4882a593Smuzhiyun (unsigned char *)writeSetup,
1457*4882a593Smuzhiyun (void*)pWriteBuffer,
1458*4882a593Smuzhiyun writeBufferSize,
1459*4882a593Smuzhiyun NULL,
1460*4882a593Smuzhiyun pDev );
1461*4882a593Smuzhiyun
1462*4882a593Smuzhiyun DBG( "Actual Write:\n" );
1463*4882a593Smuzhiyun PrintHex( pWriteBuffer, writeBufferSize );
1464*4882a593Smuzhiyun
1465*4882a593Smuzhiyun sema_init( &writeSem, 0 );
1466*4882a593Smuzhiyun
1467*4882a593Smuzhiyun pWriteURB->complete = WriteSyncCallback;
1468*4882a593Smuzhiyun pWriteURB->context = &writeSem;
1469*4882a593Smuzhiyun
1470*4882a593Smuzhiyun // Wake device
1471*4882a593Smuzhiyun result = usb_autopm_get_interface( pDev->mpIntf );
1472*4882a593Smuzhiyun if (result < 0)
1473*4882a593Smuzhiyun {
1474*4882a593Smuzhiyun DBG( "unable to resume interface: %d\n", result );
1475*4882a593Smuzhiyun
1476*4882a593Smuzhiyun // Likely caused by device going from autosuspend -> full suspend
1477*4882a593Smuzhiyun if (result == -EPERM)
1478*4882a593Smuzhiyun {
1479*4882a593Smuzhiyun #ifdef CONFIG_PM
1480*4882a593Smuzhiyun #if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,33 ))
1481*4882a593Smuzhiyun #if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,18 ))
1482*4882a593Smuzhiyun pDev->mpNetDev->udev->auto_pm = 0;
1483*4882a593Smuzhiyun #endif
1484*4882a593Smuzhiyun #endif
1485*4882a593Smuzhiyun QuecGobiNetSuspend( pDev->mpIntf, PMSG_SUSPEND );
1486*4882a593Smuzhiyun #endif /* CONFIG_PM */
1487*4882a593Smuzhiyun }
1488*4882a593Smuzhiyun usb_free_urb( pWriteURB );
1489*4882a593Smuzhiyun kfree(writeSetup);
1490*4882a593Smuzhiyun
1491*4882a593Smuzhiyun return result;
1492*4882a593Smuzhiyun }
1493*4882a593Smuzhiyun
1494*4882a593Smuzhiyun // Critical section
1495*4882a593Smuzhiyun spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
1496*4882a593Smuzhiyun
1497*4882a593Smuzhiyun if (AddToURBList( pDev, clientID, pWriteURB ) == false)
1498*4882a593Smuzhiyun {
1499*4882a593Smuzhiyun usb_free_urb( pWriteURB );
1500*4882a593Smuzhiyun kfree(writeSetup);
1501*4882a593Smuzhiyun
1502*4882a593Smuzhiyun // End critical section
1503*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
1504*4882a593Smuzhiyun usb_autopm_put_interface( pDev->mpIntf );
1505*4882a593Smuzhiyun return -EINVAL;
1506*4882a593Smuzhiyun }
1507*4882a593Smuzhiyun
1508*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
1509*4882a593Smuzhiyun result = usb_submit_urb( pWriteURB, GFP_KERNEL );
1510*4882a593Smuzhiyun spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
1511*4882a593Smuzhiyun
1512*4882a593Smuzhiyun if (result < 0)
1513*4882a593Smuzhiyun {
1514*4882a593Smuzhiyun DBG( "submit URB error %d\n", result );
1515*4882a593Smuzhiyun
1516*4882a593Smuzhiyun // Get URB back so we can destroy it
1517*4882a593Smuzhiyun if (PopFromURBList( pDev, clientID ) != pWriteURB)
1518*4882a593Smuzhiyun {
1519*4882a593Smuzhiyun // This shouldn't happen
1520*4882a593Smuzhiyun DBG( "Didn't get write URB back\n" );
1521*4882a593Smuzhiyun //advoid ReleaseClientID() free again (no PopFromURBList)
1522*4882a593Smuzhiyun }
1523*4882a593Smuzhiyun else
1524*4882a593Smuzhiyun {
1525*4882a593Smuzhiyun usb_free_urb( pWriteURB );
1526*4882a593Smuzhiyun kfree(writeSetup);
1527*4882a593Smuzhiyun }
1528*4882a593Smuzhiyun
1529*4882a593Smuzhiyun // End critical section
1530*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
1531*4882a593Smuzhiyun usb_autopm_put_interface( pDev->mpIntf );
1532*4882a593Smuzhiyun return result;
1533*4882a593Smuzhiyun }
1534*4882a593Smuzhiyun
1535*4882a593Smuzhiyun // End critical section while we block
1536*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
1537*4882a593Smuzhiyun
1538*4882a593Smuzhiyun // Wait for write to finish
1539*4882a593Smuzhiyun if (1 != 0) //(interruptible != 0)
1540*4882a593Smuzhiyun {
1541*4882a593Smuzhiyun // Allow user interrupts
1542*4882a593Smuzhiyun result = down_interruptible( &writeSem );
1543*4882a593Smuzhiyun //if (result) INFO("down_interruptible = %d\n", result);
1544*4882a593Smuzhiyun if (result == -EINTR) {
1545*4882a593Smuzhiyun result = down_timeout(&writeSem, msecs_to_jiffies(200));
1546*4882a593Smuzhiyun //if (result) INFO("down_interruptible = %d\n", result);
1547*4882a593Smuzhiyun }
1548*4882a593Smuzhiyun }
1549*4882a593Smuzhiyun else
1550*4882a593Smuzhiyun {
1551*4882a593Smuzhiyun // Ignore user interrupts
1552*4882a593Smuzhiyun result = 0;
1553*4882a593Smuzhiyun down( &writeSem );
1554*4882a593Smuzhiyun }
1555*4882a593Smuzhiyun
1556*4882a593Smuzhiyun // Write is done, release device
1557*4882a593Smuzhiyun usb_autopm_put_interface( pDev->mpIntf );
1558*4882a593Smuzhiyun
1559*4882a593Smuzhiyun // Verify device is still valid
1560*4882a593Smuzhiyun if (IsDeviceValid( pDev ) == false)
1561*4882a593Smuzhiyun {
1562*4882a593Smuzhiyun DBG( "Invalid device!\n" );
1563*4882a593Smuzhiyun
1564*4882a593Smuzhiyun usb_kill_urb( pWriteURB );
1565*4882a593Smuzhiyun #if 0 //advoid ReleaseClientID() free again (no PopFromURBList)
1566*4882a593Smuzhiyun usb_free_urb( pWriteURB );
1567*4882a593Smuzhiyun kfree(writeSetup);
1568*4882a593Smuzhiyun #endif
1569*4882a593Smuzhiyun return -ENXIO;
1570*4882a593Smuzhiyun }
1571*4882a593Smuzhiyun
1572*4882a593Smuzhiyun // Restart critical section
1573*4882a593Smuzhiyun spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
1574*4882a593Smuzhiyun
1575*4882a593Smuzhiyun // Get URB back so we can destroy it
1576*4882a593Smuzhiyun if (PopFromURBList( pDev, clientID ) != pWriteURB)
1577*4882a593Smuzhiyun {
1578*4882a593Smuzhiyun // This shouldn't happen
1579*4882a593Smuzhiyun DBG( "Didn't get write URB back\n" );
1580*4882a593Smuzhiyun
1581*4882a593Smuzhiyun // End critical section
1582*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
1583*4882a593Smuzhiyun usb_kill_urb( pWriteURB );
1584*4882a593Smuzhiyun #if 0 //advoid ReleaseClientID() free again (fail PopFromURBList)
1585*4882a593Smuzhiyun usb_free_urb( pWriteURB );
1586*4882a593Smuzhiyun kfree(writeSetup);
1587*4882a593Smuzhiyun #endif
1588*4882a593Smuzhiyun return -EINVAL;
1589*4882a593Smuzhiyun }
1590*4882a593Smuzhiyun
1591*4882a593Smuzhiyun // End critical section
1592*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
1593*4882a593Smuzhiyun
1594*4882a593Smuzhiyun if (result == 0)
1595*4882a593Smuzhiyun {
1596*4882a593Smuzhiyun // Write is finished
1597*4882a593Smuzhiyun if (pWriteURB->status == 0)
1598*4882a593Smuzhiyun {
1599*4882a593Smuzhiyun // Return number of bytes that were supposed to have been written,
1600*4882a593Smuzhiyun // not size of QMI request
1601*4882a593Smuzhiyun result = writeBufferSize;
1602*4882a593Smuzhiyun }
1603*4882a593Smuzhiyun else
1604*4882a593Smuzhiyun {
1605*4882a593Smuzhiyun DBG( "bad status = %d\n", pWriteURB->status );
1606*4882a593Smuzhiyun
1607*4882a593Smuzhiyun // Return error value
1608*4882a593Smuzhiyun result = pWriteURB->status;
1609*4882a593Smuzhiyun }
1610*4882a593Smuzhiyun }
1611*4882a593Smuzhiyun else
1612*4882a593Smuzhiyun {
1613*4882a593Smuzhiyun // We have been forcibly interrupted
1614*4882a593Smuzhiyun DBG( "Interrupted %d !!!\n", result );
1615*4882a593Smuzhiyun DBG( "Device may be in bad state and need reset !!!\n" );
1616*4882a593Smuzhiyun
1617*4882a593Smuzhiyun // URB has not finished
1618*4882a593Smuzhiyun usb_kill_urb( pWriteURB );
1619*4882a593Smuzhiyun }
1620*4882a593Smuzhiyun
1621*4882a593Smuzhiyun usb_free_urb( pWriteURB );
1622*4882a593Smuzhiyun kfree(writeSetup);
1623*4882a593Smuzhiyun
1624*4882a593Smuzhiyun return result;
1625*4882a593Smuzhiyun }
1626*4882a593Smuzhiyun
1627*4882a593Smuzhiyun /*=========================================================================*/
1628*4882a593Smuzhiyun // Internal memory management functions
1629*4882a593Smuzhiyun /*=========================================================================*/
1630*4882a593Smuzhiyun
1631*4882a593Smuzhiyun /*===========================================================================
1632*4882a593Smuzhiyun METHOD:
1633*4882a593Smuzhiyun GetClientID (Public Method)
1634*4882a593Smuzhiyun
1635*4882a593Smuzhiyun DESCRIPTION:
1636*4882a593Smuzhiyun Request a QMI client for the input service type and initialize memory
1637*4882a593Smuzhiyun structure
1638*4882a593Smuzhiyun
1639*4882a593Smuzhiyun PARAMETERS:
1640*4882a593Smuzhiyun pDev [ I ] - Device specific memory
1641*4882a593Smuzhiyun serviceType [ I ] - Desired QMI service type
1642*4882a593Smuzhiyun
1643*4882a593Smuzhiyun RETURN VALUE:
1644*4882a593Smuzhiyun int - Client ID for success (positive)
1645*4882a593Smuzhiyun Negative errno for error
1646*4882a593Smuzhiyun ===========================================================================*/
1647*4882a593Smuzhiyun static int GetClientID(
1648*4882a593Smuzhiyun sGobiUSBNet * pDev,
1649*4882a593Smuzhiyun u8 serviceType )
1650*4882a593Smuzhiyun {
1651*4882a593Smuzhiyun u16 clientID;
1652*4882a593Smuzhiyun sClientMemList ** ppClientMem;
1653*4882a593Smuzhiyun int result;
1654*4882a593Smuzhiyun void * pWriteBuffer;
1655*4882a593Smuzhiyun u16 writeBufferSize;
1656*4882a593Smuzhiyun void * pReadBuffer;
1657*4882a593Smuzhiyun u16 readBufferSize;
1658*4882a593Smuzhiyun unsigned long flags;
1659*4882a593Smuzhiyun u8 transactionID;
1660*4882a593Smuzhiyun
1661*4882a593Smuzhiyun if (IsDeviceValid( pDev ) == false)
1662*4882a593Smuzhiyun {
1663*4882a593Smuzhiyun DBG( "Invalid device!\n" );
1664*4882a593Smuzhiyun return -ENXIO;
1665*4882a593Smuzhiyun }
1666*4882a593Smuzhiyun
1667*4882a593Smuzhiyun // Run QMI request to be asigned a Client ID
1668*4882a593Smuzhiyun if (serviceType != 0)
1669*4882a593Smuzhiyun {
1670*4882a593Smuzhiyun writeBufferSize = QMICTLGetClientIDReqSize();
1671*4882a593Smuzhiyun pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL );
1672*4882a593Smuzhiyun if (pWriteBuffer == NULL)
1673*4882a593Smuzhiyun {
1674*4882a593Smuzhiyun return -ENOMEM;
1675*4882a593Smuzhiyun }
1676*4882a593Smuzhiyun
1677*4882a593Smuzhiyun transactionID = QMIXactionIDGet( pDev );
1678*4882a593Smuzhiyun
1679*4882a593Smuzhiyun result = QMICTLGetClientIDReq( pWriteBuffer,
1680*4882a593Smuzhiyun writeBufferSize,
1681*4882a593Smuzhiyun transactionID,
1682*4882a593Smuzhiyun serviceType );
1683*4882a593Smuzhiyun if (result < 0)
1684*4882a593Smuzhiyun {
1685*4882a593Smuzhiyun kfree( pWriteBuffer );
1686*4882a593Smuzhiyun return result;
1687*4882a593Smuzhiyun }
1688*4882a593Smuzhiyun
1689*4882a593Smuzhiyun
1690*4882a593Smuzhiyun result = WriteSync( pDev,
1691*4882a593Smuzhiyun pWriteBuffer,
1692*4882a593Smuzhiyun writeBufferSize,
1693*4882a593Smuzhiyun QMICTL );
1694*4882a593Smuzhiyun kfree( pWriteBuffer );
1695*4882a593Smuzhiyun
1696*4882a593Smuzhiyun if (result < 0)
1697*4882a593Smuzhiyun {
1698*4882a593Smuzhiyun return result;
1699*4882a593Smuzhiyun }
1700*4882a593Smuzhiyun
1701*4882a593Smuzhiyun result = ReadSync( pDev,
1702*4882a593Smuzhiyun &pReadBuffer,
1703*4882a593Smuzhiyun QMICTL,
1704*4882a593Smuzhiyun transactionID );
1705*4882a593Smuzhiyun if (result < 0)
1706*4882a593Smuzhiyun {
1707*4882a593Smuzhiyun DBG( "bad read data %d\n", result );
1708*4882a593Smuzhiyun return result;
1709*4882a593Smuzhiyun }
1710*4882a593Smuzhiyun readBufferSize = result;
1711*4882a593Smuzhiyun
1712*4882a593Smuzhiyun result = QMICTLGetClientIDResp( pReadBuffer,
1713*4882a593Smuzhiyun readBufferSize,
1714*4882a593Smuzhiyun &clientID );
1715*4882a593Smuzhiyun
1716*4882a593Smuzhiyun /* Upon return from QMICTLGetClientIDResp, clientID
1717*4882a593Smuzhiyun * low address contains the Service Number (SN), and
1718*4882a593Smuzhiyun * clientID high address contains Client Number (CN)
1719*4882a593Smuzhiyun * For the ReadCallback to function correctly,we swap
1720*4882a593Smuzhiyun * the SN and CN on a Big Endian architecture.
1721*4882a593Smuzhiyun */
1722*4882a593Smuzhiyun clientID = le16_to_cpu(clientID);
1723*4882a593Smuzhiyun
1724*4882a593Smuzhiyun kfree( pReadBuffer );
1725*4882a593Smuzhiyun
1726*4882a593Smuzhiyun if (result < 0)
1727*4882a593Smuzhiyun {
1728*4882a593Smuzhiyun return result;
1729*4882a593Smuzhiyun }
1730*4882a593Smuzhiyun }
1731*4882a593Smuzhiyun else
1732*4882a593Smuzhiyun {
1733*4882a593Smuzhiyun // QMI CTL will always have client ID 0
1734*4882a593Smuzhiyun clientID = 0;
1735*4882a593Smuzhiyun }
1736*4882a593Smuzhiyun
1737*4882a593Smuzhiyun // Critical section
1738*4882a593Smuzhiyun spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
1739*4882a593Smuzhiyun
1740*4882a593Smuzhiyun // Verify client is not already allocated
1741*4882a593Smuzhiyun if (FindClientMem( pDev, clientID ) != NULL)
1742*4882a593Smuzhiyun {
1743*4882a593Smuzhiyun DBG( "Client memory already exists\n" );
1744*4882a593Smuzhiyun
1745*4882a593Smuzhiyun // End Critical section
1746*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
1747*4882a593Smuzhiyun return -ETOOMANYREFS;
1748*4882a593Smuzhiyun }
1749*4882a593Smuzhiyun
1750*4882a593Smuzhiyun // Go to last entry in client mem list
1751*4882a593Smuzhiyun ppClientMem = &pDev->mQMIDev.mpClientMemList;
1752*4882a593Smuzhiyun while (*ppClientMem != NULL)
1753*4882a593Smuzhiyun {
1754*4882a593Smuzhiyun ppClientMem = &(*ppClientMem)->mpNext;
1755*4882a593Smuzhiyun }
1756*4882a593Smuzhiyun
1757*4882a593Smuzhiyun // Create locations for read to place data into
1758*4882a593Smuzhiyun *ppClientMem = kmalloc( sizeof( sClientMemList ), GFP_ATOMIC );
1759*4882a593Smuzhiyun if (*ppClientMem == NULL)
1760*4882a593Smuzhiyun {
1761*4882a593Smuzhiyun DBG( "Error allocating read list\n" );
1762*4882a593Smuzhiyun
1763*4882a593Smuzhiyun // End critical section
1764*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
1765*4882a593Smuzhiyun return -ENOMEM;
1766*4882a593Smuzhiyun }
1767*4882a593Smuzhiyun
1768*4882a593Smuzhiyun (*ppClientMem)->mClientID = clientID;
1769*4882a593Smuzhiyun (*ppClientMem)->mpList = NULL;
1770*4882a593Smuzhiyun (*ppClientMem)->mpReadNotifyList = NULL;
1771*4882a593Smuzhiyun (*ppClientMem)->mpURBList = NULL;
1772*4882a593Smuzhiyun (*ppClientMem)->mpNext = NULL;
1773*4882a593Smuzhiyun
1774*4882a593Smuzhiyun // Initialize workqueue for poll()
1775*4882a593Smuzhiyun init_waitqueue_head( &(*ppClientMem)->mWaitQueue );
1776*4882a593Smuzhiyun
1777*4882a593Smuzhiyun // End Critical section
1778*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
1779*4882a593Smuzhiyun
1780*4882a593Smuzhiyun return (int)( (*ppClientMem)->mClientID );
1781*4882a593Smuzhiyun }
1782*4882a593Smuzhiyun
1783*4882a593Smuzhiyun /*===========================================================================
1784*4882a593Smuzhiyun METHOD:
1785*4882a593Smuzhiyun ReleaseClientID (Public Method)
1786*4882a593Smuzhiyun
1787*4882a593Smuzhiyun DESCRIPTION:
1788*4882a593Smuzhiyun Release QMI client and free memory
1789*4882a593Smuzhiyun
1790*4882a593Smuzhiyun PARAMETERS:
1791*4882a593Smuzhiyun pDev [ I ] - Device specific memory
1792*4882a593Smuzhiyun clientID [ I ] - Requester's client ID
1793*4882a593Smuzhiyun
1794*4882a593Smuzhiyun RETURN VALUE:
1795*4882a593Smuzhiyun None
1796*4882a593Smuzhiyun ===========================================================================*/
1797*4882a593Smuzhiyun static void ReleaseClientID(
1798*4882a593Smuzhiyun sGobiUSBNet * pDev,
1799*4882a593Smuzhiyun u16 clientID )
1800*4882a593Smuzhiyun {
1801*4882a593Smuzhiyun int result;
1802*4882a593Smuzhiyun sClientMemList ** ppDelClientMem;
1803*4882a593Smuzhiyun sClientMemList * pNextClientMem;
1804*4882a593Smuzhiyun struct urb * pDelURB;
1805*4882a593Smuzhiyun void * pDelData;
1806*4882a593Smuzhiyun u16 dataSize;
1807*4882a593Smuzhiyun void * pWriteBuffer;
1808*4882a593Smuzhiyun u16 writeBufferSize;
1809*4882a593Smuzhiyun void * pReadBuffer;
1810*4882a593Smuzhiyun u16 readBufferSize;
1811*4882a593Smuzhiyun unsigned long flags;
1812*4882a593Smuzhiyun u8 transactionID;
1813*4882a593Smuzhiyun
1814*4882a593Smuzhiyun // Is device is still valid?
1815*4882a593Smuzhiyun if (IsDeviceValid( pDev ) == false)
1816*4882a593Smuzhiyun {
1817*4882a593Smuzhiyun DBG( "invalid device\n" );
1818*4882a593Smuzhiyun return;
1819*4882a593Smuzhiyun }
1820*4882a593Smuzhiyun
1821*4882a593Smuzhiyun DBG( "releasing 0x%04X\n", clientID );
1822*4882a593Smuzhiyun
1823*4882a593Smuzhiyun // Run QMI ReleaseClientID if this isn't QMICTL
1824*4882a593Smuzhiyun if (clientID != QMICTL && pDev->mpNetDev->udev->state)
1825*4882a593Smuzhiyun {
1826*4882a593Smuzhiyun // Note: all errors are non fatal, as we always want to delete
1827*4882a593Smuzhiyun // client memory in latter part of function
1828*4882a593Smuzhiyun
1829*4882a593Smuzhiyun writeBufferSize = QMICTLReleaseClientIDReqSize();
1830*4882a593Smuzhiyun pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL );
1831*4882a593Smuzhiyun if (pWriteBuffer == NULL)
1832*4882a593Smuzhiyun {
1833*4882a593Smuzhiyun DBG( "memory error\n" );
1834*4882a593Smuzhiyun }
1835*4882a593Smuzhiyun else
1836*4882a593Smuzhiyun {
1837*4882a593Smuzhiyun transactionID = QMIXactionIDGet( pDev );
1838*4882a593Smuzhiyun
1839*4882a593Smuzhiyun result = QMICTLReleaseClientIDReq( pWriteBuffer,
1840*4882a593Smuzhiyun writeBufferSize,
1841*4882a593Smuzhiyun transactionID,
1842*4882a593Smuzhiyun clientID );
1843*4882a593Smuzhiyun if (result < 0)
1844*4882a593Smuzhiyun {
1845*4882a593Smuzhiyun kfree( pWriteBuffer );
1846*4882a593Smuzhiyun DBG( "error %d filling req buffer\n", result );
1847*4882a593Smuzhiyun }
1848*4882a593Smuzhiyun else
1849*4882a593Smuzhiyun {
1850*4882a593Smuzhiyun result = WriteSync( pDev,
1851*4882a593Smuzhiyun pWriteBuffer,
1852*4882a593Smuzhiyun writeBufferSize,
1853*4882a593Smuzhiyun QMICTL );
1854*4882a593Smuzhiyun kfree( pWriteBuffer );
1855*4882a593Smuzhiyun
1856*4882a593Smuzhiyun if (result < 0)
1857*4882a593Smuzhiyun {
1858*4882a593Smuzhiyun DBG( "bad write status %d\n", result );
1859*4882a593Smuzhiyun }
1860*4882a593Smuzhiyun else
1861*4882a593Smuzhiyun {
1862*4882a593Smuzhiyun result = ReadSync( pDev,
1863*4882a593Smuzhiyun &pReadBuffer,
1864*4882a593Smuzhiyun QMICTL,
1865*4882a593Smuzhiyun transactionID );
1866*4882a593Smuzhiyun if (result < 0)
1867*4882a593Smuzhiyun {
1868*4882a593Smuzhiyun DBG( "bad read status %d\n", result );
1869*4882a593Smuzhiyun }
1870*4882a593Smuzhiyun else
1871*4882a593Smuzhiyun {
1872*4882a593Smuzhiyun readBufferSize = result;
1873*4882a593Smuzhiyun
1874*4882a593Smuzhiyun result = QMICTLReleaseClientIDResp( pReadBuffer,
1875*4882a593Smuzhiyun readBufferSize );
1876*4882a593Smuzhiyun kfree( pReadBuffer );
1877*4882a593Smuzhiyun
1878*4882a593Smuzhiyun if (result < 0)
1879*4882a593Smuzhiyun {
1880*4882a593Smuzhiyun DBG( "error %d parsing response\n", result );
1881*4882a593Smuzhiyun }
1882*4882a593Smuzhiyun }
1883*4882a593Smuzhiyun }
1884*4882a593Smuzhiyun }
1885*4882a593Smuzhiyun }
1886*4882a593Smuzhiyun }
1887*4882a593Smuzhiyun
1888*4882a593Smuzhiyun // Cleaning up client memory
1889*4882a593Smuzhiyun
1890*4882a593Smuzhiyun // Critical section
1891*4882a593Smuzhiyun spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
1892*4882a593Smuzhiyun
1893*4882a593Smuzhiyun // Can't use FindClientMem, I need to keep pointer of previous
1894*4882a593Smuzhiyun ppDelClientMem = &pDev->mQMIDev.mpClientMemList;
1895*4882a593Smuzhiyun while (*ppDelClientMem != NULL)
1896*4882a593Smuzhiyun {
1897*4882a593Smuzhiyun if ((*ppDelClientMem)->mClientID == clientID)
1898*4882a593Smuzhiyun {
1899*4882a593Smuzhiyun pNextClientMem = (*ppDelClientMem)->mpNext;
1900*4882a593Smuzhiyun
1901*4882a593Smuzhiyun // Notify all clients
1902*4882a593Smuzhiyun while (NotifyAndPopNotifyList( pDev,
1903*4882a593Smuzhiyun clientID,
1904*4882a593Smuzhiyun 0 ) == true );
1905*4882a593Smuzhiyun
1906*4882a593Smuzhiyun // Kill and free all URB's
1907*4882a593Smuzhiyun pDelURB = PopFromURBList( pDev, clientID );
1908*4882a593Smuzhiyun while (pDelURB != NULL)
1909*4882a593Smuzhiyun {
1910*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
1911*4882a593Smuzhiyun usb_kill_urb( pDelURB );
1912*4882a593Smuzhiyun usb_free_urb( pDelURB );
1913*4882a593Smuzhiyun spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
1914*4882a593Smuzhiyun pDelURB = PopFromURBList( pDev, clientID );
1915*4882a593Smuzhiyun }
1916*4882a593Smuzhiyun
1917*4882a593Smuzhiyun // Free any unread data
1918*4882a593Smuzhiyun while (PopFromReadMemList( pDev,
1919*4882a593Smuzhiyun clientID,
1920*4882a593Smuzhiyun 0,
1921*4882a593Smuzhiyun &pDelData,
1922*4882a593Smuzhiyun &dataSize ) == true )
1923*4882a593Smuzhiyun {
1924*4882a593Smuzhiyun kfree( pDelData );
1925*4882a593Smuzhiyun }
1926*4882a593Smuzhiyun
1927*4882a593Smuzhiyun // Delete client Mem
1928*4882a593Smuzhiyun if (!waitqueue_active( &(*ppDelClientMem)->mWaitQueue))
1929*4882a593Smuzhiyun kfree( *ppDelClientMem );
1930*4882a593Smuzhiyun else
1931*4882a593Smuzhiyun INFO("memory leak!\n");
1932*4882a593Smuzhiyun
1933*4882a593Smuzhiyun // Overwrite the pointer that was to this client mem
1934*4882a593Smuzhiyun *ppDelClientMem = pNextClientMem;
1935*4882a593Smuzhiyun }
1936*4882a593Smuzhiyun else
1937*4882a593Smuzhiyun {
1938*4882a593Smuzhiyun // I now point to (a pointer of ((the node I was at)'s mpNext))
1939*4882a593Smuzhiyun ppDelClientMem = &(*ppDelClientMem)->mpNext;
1940*4882a593Smuzhiyun }
1941*4882a593Smuzhiyun }
1942*4882a593Smuzhiyun
1943*4882a593Smuzhiyun // End Critical section
1944*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
1945*4882a593Smuzhiyun
1946*4882a593Smuzhiyun return;
1947*4882a593Smuzhiyun }
1948*4882a593Smuzhiyun
1949*4882a593Smuzhiyun /*===========================================================================
1950*4882a593Smuzhiyun METHOD:
1951*4882a593Smuzhiyun FindClientMem (Public Method)
1952*4882a593Smuzhiyun
1953*4882a593Smuzhiyun DESCRIPTION:
1954*4882a593Smuzhiyun Find this client's memory
1955*4882a593Smuzhiyun
1956*4882a593Smuzhiyun Caller MUST have lock on mClientMemLock
1957*4882a593Smuzhiyun
1958*4882a593Smuzhiyun PARAMETERS:
1959*4882a593Smuzhiyun pDev [ I ] - Device specific memory
1960*4882a593Smuzhiyun clientID [ I ] - Requester's client ID
1961*4882a593Smuzhiyun
1962*4882a593Smuzhiyun RETURN VALUE:
1963*4882a593Smuzhiyun sClientMemList - Pointer to requested sClientMemList for success
1964*4882a593Smuzhiyun NULL for error
1965*4882a593Smuzhiyun ===========================================================================*/
1966*4882a593Smuzhiyun static sClientMemList * FindClientMem(
1967*4882a593Smuzhiyun sGobiUSBNet * pDev,
1968*4882a593Smuzhiyun u16 clientID )
1969*4882a593Smuzhiyun {
1970*4882a593Smuzhiyun sClientMemList * pClientMem;
1971*4882a593Smuzhiyun
1972*4882a593Smuzhiyun if (IsDeviceValid( pDev ) == false)
1973*4882a593Smuzhiyun {
1974*4882a593Smuzhiyun DBG( "Invalid device\n" );
1975*4882a593Smuzhiyun return NULL;
1976*4882a593Smuzhiyun }
1977*4882a593Smuzhiyun
1978*4882a593Smuzhiyun #ifdef CONFIG_SMP
1979*4882a593Smuzhiyun // Verify Lock
1980*4882a593Smuzhiyun if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0)
1981*4882a593Smuzhiyun {
1982*4882a593Smuzhiyun DBG( "unlocked\n" );
1983*4882a593Smuzhiyun BUG();
1984*4882a593Smuzhiyun }
1985*4882a593Smuzhiyun #endif
1986*4882a593Smuzhiyun
1987*4882a593Smuzhiyun pClientMem = pDev->mQMIDev.mpClientMemList;
1988*4882a593Smuzhiyun while (pClientMem != NULL)
1989*4882a593Smuzhiyun {
1990*4882a593Smuzhiyun if (pClientMem->mClientID == clientID)
1991*4882a593Smuzhiyun {
1992*4882a593Smuzhiyun // Success
1993*4882a593Smuzhiyun VDBG("Found client's 0x%x memory\n", clientID);
1994*4882a593Smuzhiyun return pClientMem;
1995*4882a593Smuzhiyun }
1996*4882a593Smuzhiyun
1997*4882a593Smuzhiyun pClientMem = pClientMem->mpNext;
1998*4882a593Smuzhiyun }
1999*4882a593Smuzhiyun
2000*4882a593Smuzhiyun DBG( "Could not find client mem 0x%04X\n", clientID );
2001*4882a593Smuzhiyun return NULL;
2002*4882a593Smuzhiyun }
2003*4882a593Smuzhiyun
2004*4882a593Smuzhiyun /*===========================================================================
2005*4882a593Smuzhiyun METHOD:
2006*4882a593Smuzhiyun AddToReadMemList (Public Method)
2007*4882a593Smuzhiyun
2008*4882a593Smuzhiyun DESCRIPTION:
2009*4882a593Smuzhiyun Add Data to this client's ReadMem list
2010*4882a593Smuzhiyun
2011*4882a593Smuzhiyun Caller MUST have lock on mClientMemLock
2012*4882a593Smuzhiyun
2013*4882a593Smuzhiyun PARAMETERS:
2014*4882a593Smuzhiyun pDev [ I ] - Device specific memory
2015*4882a593Smuzhiyun clientID [ I ] - Requester's client ID
2016*4882a593Smuzhiyun transactionID [ I ] - Transaction ID or 0 for any
2017*4882a593Smuzhiyun pData [ I ] - Data to add
2018*4882a593Smuzhiyun dataSize [ I ] - Size of data to add
2019*4882a593Smuzhiyun
2020*4882a593Smuzhiyun RETURN VALUE:
2021*4882a593Smuzhiyun bool
2022*4882a593Smuzhiyun ===========================================================================*/
2023*4882a593Smuzhiyun static bool AddToReadMemList(
2024*4882a593Smuzhiyun sGobiUSBNet * pDev,
2025*4882a593Smuzhiyun u16 clientID,
2026*4882a593Smuzhiyun u16 transactionID,
2027*4882a593Smuzhiyun void * pData,
2028*4882a593Smuzhiyun u16 dataSize )
2029*4882a593Smuzhiyun {
2030*4882a593Smuzhiyun sClientMemList * pClientMem;
2031*4882a593Smuzhiyun sReadMemList ** ppThisReadMemList;
2032*4882a593Smuzhiyun
2033*4882a593Smuzhiyun #ifdef CONFIG_SMP
2034*4882a593Smuzhiyun // Verify Lock
2035*4882a593Smuzhiyun if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0)
2036*4882a593Smuzhiyun {
2037*4882a593Smuzhiyun DBG( "unlocked\n" );
2038*4882a593Smuzhiyun BUG();
2039*4882a593Smuzhiyun }
2040*4882a593Smuzhiyun #endif
2041*4882a593Smuzhiyun
2042*4882a593Smuzhiyun // Get this client's memory location
2043*4882a593Smuzhiyun pClientMem = FindClientMem( pDev, clientID );
2044*4882a593Smuzhiyun if (pClientMem == NULL)
2045*4882a593Smuzhiyun {
2046*4882a593Smuzhiyun DBG( "Could not find this client's memory 0x%04X\n",
2047*4882a593Smuzhiyun clientID );
2048*4882a593Smuzhiyun
2049*4882a593Smuzhiyun return false;
2050*4882a593Smuzhiyun }
2051*4882a593Smuzhiyun
2052*4882a593Smuzhiyun // Go to last ReadMemList entry
2053*4882a593Smuzhiyun ppThisReadMemList = &pClientMem->mpList;
2054*4882a593Smuzhiyun while (*ppThisReadMemList != NULL)
2055*4882a593Smuzhiyun {
2056*4882a593Smuzhiyun ppThisReadMemList = &(*ppThisReadMemList)->mpNext;
2057*4882a593Smuzhiyun }
2058*4882a593Smuzhiyun
2059*4882a593Smuzhiyun *ppThisReadMemList = kmalloc( sizeof( sReadMemList ), GFP_ATOMIC );
2060*4882a593Smuzhiyun if (*ppThisReadMemList == NULL)
2061*4882a593Smuzhiyun {
2062*4882a593Smuzhiyun DBG( "Mem error\n" );
2063*4882a593Smuzhiyun
2064*4882a593Smuzhiyun return false;
2065*4882a593Smuzhiyun }
2066*4882a593Smuzhiyun
2067*4882a593Smuzhiyun (*ppThisReadMemList)->mpNext = NULL;
2068*4882a593Smuzhiyun (*ppThisReadMemList)->mpData = pData;
2069*4882a593Smuzhiyun (*ppThisReadMemList)->mDataSize = dataSize;
2070*4882a593Smuzhiyun (*ppThisReadMemList)->mTransactionID = transactionID;
2071*4882a593Smuzhiyun
2072*4882a593Smuzhiyun return true;
2073*4882a593Smuzhiyun }
2074*4882a593Smuzhiyun
2075*4882a593Smuzhiyun /*===========================================================================
2076*4882a593Smuzhiyun METHOD:
2077*4882a593Smuzhiyun PopFromReadMemList (Public Method)
2078*4882a593Smuzhiyun
2079*4882a593Smuzhiyun DESCRIPTION:
2080*4882a593Smuzhiyun Remove data from this client's ReadMem list if it matches
2081*4882a593Smuzhiyun the specified transaction ID.
2082*4882a593Smuzhiyun
2083*4882a593Smuzhiyun Caller MUST have lock on mClientMemLock
2084*4882a593Smuzhiyun
2085*4882a593Smuzhiyun PARAMETERS:
2086*4882a593Smuzhiyun pDev [ I ] - Device specific memory
2087*4882a593Smuzhiyun clientID [ I ] - Requester's client ID
2088*4882a593Smuzhiyun transactionID [ I ] - Transaction ID or 0 for any
2089*4882a593Smuzhiyun ppData [I/O] - On success, will be filled with a
2090*4882a593Smuzhiyun pointer to read buffer
2091*4882a593Smuzhiyun pDataSize [I/O] - On succces, will be filled with the
2092*4882a593Smuzhiyun read buffer's size
2093*4882a593Smuzhiyun
2094*4882a593Smuzhiyun RETURN VALUE:
2095*4882a593Smuzhiyun bool
2096*4882a593Smuzhiyun ===========================================================================*/
2097*4882a593Smuzhiyun static bool PopFromReadMemList(
2098*4882a593Smuzhiyun sGobiUSBNet * pDev,
2099*4882a593Smuzhiyun u16 clientID,
2100*4882a593Smuzhiyun u16 transactionID,
2101*4882a593Smuzhiyun void ** ppData,
2102*4882a593Smuzhiyun u16 * pDataSize )
2103*4882a593Smuzhiyun {
2104*4882a593Smuzhiyun sClientMemList * pClientMem;
2105*4882a593Smuzhiyun sReadMemList * pDelReadMemList, ** ppReadMemList;
2106*4882a593Smuzhiyun
2107*4882a593Smuzhiyun #ifdef CONFIG_SMP
2108*4882a593Smuzhiyun // Verify Lock
2109*4882a593Smuzhiyun if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0)
2110*4882a593Smuzhiyun {
2111*4882a593Smuzhiyun DBG( "unlocked\n" );
2112*4882a593Smuzhiyun BUG();
2113*4882a593Smuzhiyun }
2114*4882a593Smuzhiyun #endif
2115*4882a593Smuzhiyun
2116*4882a593Smuzhiyun // Get this client's memory location
2117*4882a593Smuzhiyun pClientMem = FindClientMem( pDev, clientID );
2118*4882a593Smuzhiyun if (pClientMem == NULL)
2119*4882a593Smuzhiyun {
2120*4882a593Smuzhiyun DBG( "Could not find this client's memory 0x%04X\n",
2121*4882a593Smuzhiyun clientID );
2122*4882a593Smuzhiyun
2123*4882a593Smuzhiyun return false;
2124*4882a593Smuzhiyun }
2125*4882a593Smuzhiyun
2126*4882a593Smuzhiyun ppReadMemList = &(pClientMem->mpList);
2127*4882a593Smuzhiyun pDelReadMemList = NULL;
2128*4882a593Smuzhiyun
2129*4882a593Smuzhiyun // Find first message that matches this transaction ID
2130*4882a593Smuzhiyun while (*ppReadMemList != NULL)
2131*4882a593Smuzhiyun {
2132*4882a593Smuzhiyun // Do we care about transaction ID?
2133*4882a593Smuzhiyun if (transactionID == 0
2134*4882a593Smuzhiyun || transactionID == (*ppReadMemList)->mTransactionID )
2135*4882a593Smuzhiyun {
2136*4882a593Smuzhiyun pDelReadMemList = *ppReadMemList;
2137*4882a593Smuzhiyun VDBG( "*ppReadMemList = 0x%p pDelReadMemList = 0x%p\n",
2138*4882a593Smuzhiyun *ppReadMemList, pDelReadMemList );
2139*4882a593Smuzhiyun break;
2140*4882a593Smuzhiyun }
2141*4882a593Smuzhiyun
2142*4882a593Smuzhiyun VDBG( "skipping 0x%04X data TID = %x\n", clientID, (*ppReadMemList)->mTransactionID );
2143*4882a593Smuzhiyun
2144*4882a593Smuzhiyun // Next
2145*4882a593Smuzhiyun ppReadMemList = &(*ppReadMemList)->mpNext;
2146*4882a593Smuzhiyun }
2147*4882a593Smuzhiyun VDBG( "*ppReadMemList = 0x%p pDelReadMemList = 0x%p\n",
2148*4882a593Smuzhiyun *ppReadMemList, pDelReadMemList );
2149*4882a593Smuzhiyun if (pDelReadMemList != NULL)
2150*4882a593Smuzhiyun {
2151*4882a593Smuzhiyun *ppReadMemList = (*ppReadMemList)->mpNext;
2152*4882a593Smuzhiyun
2153*4882a593Smuzhiyun // Copy to output
2154*4882a593Smuzhiyun *ppData = pDelReadMemList->mpData;
2155*4882a593Smuzhiyun *pDataSize = pDelReadMemList->mDataSize;
2156*4882a593Smuzhiyun VDBG( "*ppData = 0x%p pDataSize = %u\n",
2157*4882a593Smuzhiyun *ppData, *pDataSize );
2158*4882a593Smuzhiyun
2159*4882a593Smuzhiyun // Free memory
2160*4882a593Smuzhiyun kfree( pDelReadMemList );
2161*4882a593Smuzhiyun
2162*4882a593Smuzhiyun return true;
2163*4882a593Smuzhiyun }
2164*4882a593Smuzhiyun else
2165*4882a593Smuzhiyun {
2166*4882a593Smuzhiyun DBG( "No read memory to pop, Client 0x%04X, TID = %x\n",
2167*4882a593Smuzhiyun clientID,
2168*4882a593Smuzhiyun transactionID );
2169*4882a593Smuzhiyun return false;
2170*4882a593Smuzhiyun }
2171*4882a593Smuzhiyun }
2172*4882a593Smuzhiyun
2173*4882a593Smuzhiyun /*===========================================================================
2174*4882a593Smuzhiyun METHOD:
2175*4882a593Smuzhiyun AddToNotifyList (Public Method)
2176*4882a593Smuzhiyun
2177*4882a593Smuzhiyun DESCRIPTION:
2178*4882a593Smuzhiyun Add Notify entry to this client's notify List
2179*4882a593Smuzhiyun
2180*4882a593Smuzhiyun Caller MUST have lock on mClientMemLock
2181*4882a593Smuzhiyun
2182*4882a593Smuzhiyun PARAMETERS:
2183*4882a593Smuzhiyun pDev [ I ] - Device specific memory
2184*4882a593Smuzhiyun clientID [ I ] - Requester's client ID
2185*4882a593Smuzhiyun transactionID [ I ] - Transaction ID or 0 for any
2186*4882a593Smuzhiyun pNotifyFunct [ I ] - Callback function to be run when data is available
2187*4882a593Smuzhiyun pData [ I ] - Data buffer that willl be passed (unmodified)
2188*4882a593Smuzhiyun to callback
2189*4882a593Smuzhiyun
2190*4882a593Smuzhiyun RETURN VALUE:
2191*4882a593Smuzhiyun bool
2192*4882a593Smuzhiyun ===========================================================================*/
2193*4882a593Smuzhiyun static bool AddToNotifyList(
2194*4882a593Smuzhiyun sGobiUSBNet * pDev,
2195*4882a593Smuzhiyun u16 clientID,
2196*4882a593Smuzhiyun u16 transactionID,
2197*4882a593Smuzhiyun void (* pNotifyFunct)(sGobiUSBNet *, u16, void *),
2198*4882a593Smuzhiyun void * pData )
2199*4882a593Smuzhiyun {
2200*4882a593Smuzhiyun sClientMemList * pClientMem;
2201*4882a593Smuzhiyun sNotifyList ** ppThisNotifyList;
2202*4882a593Smuzhiyun
2203*4882a593Smuzhiyun #ifdef CONFIG_SMP
2204*4882a593Smuzhiyun // Verify Lock
2205*4882a593Smuzhiyun if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0)
2206*4882a593Smuzhiyun {
2207*4882a593Smuzhiyun DBG( "unlocked\n" );
2208*4882a593Smuzhiyun BUG();
2209*4882a593Smuzhiyun }
2210*4882a593Smuzhiyun #endif
2211*4882a593Smuzhiyun
2212*4882a593Smuzhiyun // Get this client's memory location
2213*4882a593Smuzhiyun pClientMem = FindClientMem( pDev, clientID );
2214*4882a593Smuzhiyun if (pClientMem == NULL)
2215*4882a593Smuzhiyun {
2216*4882a593Smuzhiyun DBG( "Could not find this client's memory 0x%04X\n", clientID );
2217*4882a593Smuzhiyun return false;
2218*4882a593Smuzhiyun }
2219*4882a593Smuzhiyun
2220*4882a593Smuzhiyun // Go to last URBList entry
2221*4882a593Smuzhiyun ppThisNotifyList = &pClientMem->mpReadNotifyList;
2222*4882a593Smuzhiyun while (*ppThisNotifyList != NULL)
2223*4882a593Smuzhiyun {
2224*4882a593Smuzhiyun ppThisNotifyList = &(*ppThisNotifyList)->mpNext;
2225*4882a593Smuzhiyun }
2226*4882a593Smuzhiyun
2227*4882a593Smuzhiyun *ppThisNotifyList = kmalloc( sizeof( sNotifyList ), GFP_ATOMIC );
2228*4882a593Smuzhiyun if (*ppThisNotifyList == NULL)
2229*4882a593Smuzhiyun {
2230*4882a593Smuzhiyun DBG( "Mem error\n" );
2231*4882a593Smuzhiyun return false;
2232*4882a593Smuzhiyun }
2233*4882a593Smuzhiyun
2234*4882a593Smuzhiyun (*ppThisNotifyList)->mpNext = NULL;
2235*4882a593Smuzhiyun (*ppThisNotifyList)->mpNotifyFunct = pNotifyFunct;
2236*4882a593Smuzhiyun (*ppThisNotifyList)->mpData = pData;
2237*4882a593Smuzhiyun (*ppThisNotifyList)->mTransactionID = transactionID;
2238*4882a593Smuzhiyun
2239*4882a593Smuzhiyun return true;
2240*4882a593Smuzhiyun }
2241*4882a593Smuzhiyun
2242*4882a593Smuzhiyun /*===========================================================================
2243*4882a593Smuzhiyun METHOD:
2244*4882a593Smuzhiyun NotifyAndPopNotifyList (Public Method)
2245*4882a593Smuzhiyun
2246*4882a593Smuzhiyun DESCRIPTION:
2247*4882a593Smuzhiyun Remove first Notify entry from this client's notify list
2248*4882a593Smuzhiyun and Run function
2249*4882a593Smuzhiyun
2250*4882a593Smuzhiyun Caller MUST have lock on mClientMemLock
2251*4882a593Smuzhiyun
2252*4882a593Smuzhiyun PARAMETERS:
2253*4882a593Smuzhiyun pDev [ I ] - Device specific memory
2254*4882a593Smuzhiyun clientID [ I ] - Requester's client ID
2255*4882a593Smuzhiyun transactionID [ I ] - Transaction ID or 0 for any
2256*4882a593Smuzhiyun
2257*4882a593Smuzhiyun RETURN VALUE:
2258*4882a593Smuzhiyun bool
2259*4882a593Smuzhiyun ===========================================================================*/
2260*4882a593Smuzhiyun static bool NotifyAndPopNotifyList(
2261*4882a593Smuzhiyun sGobiUSBNet * pDev,
2262*4882a593Smuzhiyun u16 clientID,
2263*4882a593Smuzhiyun u16 transactionID )
2264*4882a593Smuzhiyun {
2265*4882a593Smuzhiyun sClientMemList * pClientMem;
2266*4882a593Smuzhiyun sNotifyList * pDelNotifyList, ** ppNotifyList;
2267*4882a593Smuzhiyun
2268*4882a593Smuzhiyun #ifdef CONFIG_SMP
2269*4882a593Smuzhiyun // Verify Lock
2270*4882a593Smuzhiyun if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0)
2271*4882a593Smuzhiyun {
2272*4882a593Smuzhiyun DBG( "unlocked\n" );
2273*4882a593Smuzhiyun BUG();
2274*4882a593Smuzhiyun }
2275*4882a593Smuzhiyun #endif
2276*4882a593Smuzhiyun
2277*4882a593Smuzhiyun // Get this client's memory location
2278*4882a593Smuzhiyun pClientMem = FindClientMem( pDev, clientID );
2279*4882a593Smuzhiyun if (pClientMem == NULL)
2280*4882a593Smuzhiyun {
2281*4882a593Smuzhiyun DBG( "Could not find this client's memory 0x%04X\n", clientID );
2282*4882a593Smuzhiyun return false;
2283*4882a593Smuzhiyun }
2284*4882a593Smuzhiyun
2285*4882a593Smuzhiyun ppNotifyList = &(pClientMem->mpReadNotifyList);
2286*4882a593Smuzhiyun pDelNotifyList = NULL;
2287*4882a593Smuzhiyun
2288*4882a593Smuzhiyun // Remove from list
2289*4882a593Smuzhiyun while (*ppNotifyList != NULL)
2290*4882a593Smuzhiyun {
2291*4882a593Smuzhiyun // Do we care about transaction ID?
2292*4882a593Smuzhiyun if (transactionID == 0
2293*4882a593Smuzhiyun || (*ppNotifyList)->mTransactionID == 0
2294*4882a593Smuzhiyun || transactionID == (*ppNotifyList)->mTransactionID)
2295*4882a593Smuzhiyun {
2296*4882a593Smuzhiyun pDelNotifyList = *ppNotifyList;
2297*4882a593Smuzhiyun break;
2298*4882a593Smuzhiyun }
2299*4882a593Smuzhiyun
2300*4882a593Smuzhiyun DBG( "skipping data TID = %x\n", (*ppNotifyList)->mTransactionID );
2301*4882a593Smuzhiyun
2302*4882a593Smuzhiyun // next
2303*4882a593Smuzhiyun ppNotifyList = &(*ppNotifyList)->mpNext;
2304*4882a593Smuzhiyun }
2305*4882a593Smuzhiyun
2306*4882a593Smuzhiyun if (pDelNotifyList != NULL)
2307*4882a593Smuzhiyun {
2308*4882a593Smuzhiyun // Remove element
2309*4882a593Smuzhiyun *ppNotifyList = (*ppNotifyList)->mpNext;
2310*4882a593Smuzhiyun
2311*4882a593Smuzhiyun // Run notification function
2312*4882a593Smuzhiyun if (pDelNotifyList->mpNotifyFunct != NULL)
2313*4882a593Smuzhiyun {
2314*4882a593Smuzhiyun // Unlock for callback
2315*4882a593Smuzhiyun spin_unlock( &pDev->mQMIDev.mClientMemLock );
2316*4882a593Smuzhiyun
2317*4882a593Smuzhiyun pDelNotifyList->mpNotifyFunct( pDev,
2318*4882a593Smuzhiyun clientID,
2319*4882a593Smuzhiyun pDelNotifyList->mpData );
2320*4882a593Smuzhiyun
2321*4882a593Smuzhiyun // Restore lock
2322*4882a593Smuzhiyun spin_lock( &pDev->mQMIDev.mClientMemLock );
2323*4882a593Smuzhiyun }
2324*4882a593Smuzhiyun
2325*4882a593Smuzhiyun // Delete memory
2326*4882a593Smuzhiyun kfree( pDelNotifyList );
2327*4882a593Smuzhiyun
2328*4882a593Smuzhiyun return true;
2329*4882a593Smuzhiyun }
2330*4882a593Smuzhiyun else
2331*4882a593Smuzhiyun {
2332*4882a593Smuzhiyun DBG( "no one to notify for TID %x\n", transactionID );
2333*4882a593Smuzhiyun
2334*4882a593Smuzhiyun return false;
2335*4882a593Smuzhiyun }
2336*4882a593Smuzhiyun }
2337*4882a593Smuzhiyun
2338*4882a593Smuzhiyun /*===========================================================================
2339*4882a593Smuzhiyun METHOD:
2340*4882a593Smuzhiyun AddToURBList (Public Method)
2341*4882a593Smuzhiyun
2342*4882a593Smuzhiyun DESCRIPTION:
2343*4882a593Smuzhiyun Add URB to this client's URB list
2344*4882a593Smuzhiyun
2345*4882a593Smuzhiyun Caller MUST have lock on mClientMemLock
2346*4882a593Smuzhiyun
2347*4882a593Smuzhiyun PARAMETERS:
2348*4882a593Smuzhiyun pDev [ I ] - Device specific memory
2349*4882a593Smuzhiyun clientID [ I ] - Requester's client ID
2350*4882a593Smuzhiyun pURB [ I ] - URB to be added
2351*4882a593Smuzhiyun
2352*4882a593Smuzhiyun RETURN VALUE:
2353*4882a593Smuzhiyun bool
2354*4882a593Smuzhiyun ===========================================================================*/
2355*4882a593Smuzhiyun static bool AddToURBList(
2356*4882a593Smuzhiyun sGobiUSBNet * pDev,
2357*4882a593Smuzhiyun u16 clientID,
2358*4882a593Smuzhiyun struct urb * pURB )
2359*4882a593Smuzhiyun {
2360*4882a593Smuzhiyun sClientMemList * pClientMem;
2361*4882a593Smuzhiyun sURBList ** ppThisURBList;
2362*4882a593Smuzhiyun
2363*4882a593Smuzhiyun #ifdef CONFIG_SMP
2364*4882a593Smuzhiyun // Verify Lock
2365*4882a593Smuzhiyun if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0)
2366*4882a593Smuzhiyun {
2367*4882a593Smuzhiyun DBG( "unlocked\n" );
2368*4882a593Smuzhiyun BUG();
2369*4882a593Smuzhiyun }
2370*4882a593Smuzhiyun #endif
2371*4882a593Smuzhiyun
2372*4882a593Smuzhiyun // Get this client's memory location
2373*4882a593Smuzhiyun pClientMem = FindClientMem( pDev, clientID );
2374*4882a593Smuzhiyun if (pClientMem == NULL)
2375*4882a593Smuzhiyun {
2376*4882a593Smuzhiyun DBG( "Could not find this client's memory 0x%04X\n", clientID );
2377*4882a593Smuzhiyun return false;
2378*4882a593Smuzhiyun }
2379*4882a593Smuzhiyun
2380*4882a593Smuzhiyun // Go to last URBList entry
2381*4882a593Smuzhiyun ppThisURBList = &pClientMem->mpURBList;
2382*4882a593Smuzhiyun while (*ppThisURBList != NULL)
2383*4882a593Smuzhiyun {
2384*4882a593Smuzhiyun ppThisURBList = &(*ppThisURBList)->mpNext;
2385*4882a593Smuzhiyun }
2386*4882a593Smuzhiyun
2387*4882a593Smuzhiyun *ppThisURBList = kmalloc( sizeof( sURBList ), GFP_ATOMIC );
2388*4882a593Smuzhiyun if (*ppThisURBList == NULL)
2389*4882a593Smuzhiyun {
2390*4882a593Smuzhiyun DBG( "Mem error\n" );
2391*4882a593Smuzhiyun return false;
2392*4882a593Smuzhiyun }
2393*4882a593Smuzhiyun
2394*4882a593Smuzhiyun (*ppThisURBList)->mpNext = NULL;
2395*4882a593Smuzhiyun (*ppThisURBList)->mpURB = pURB;
2396*4882a593Smuzhiyun
2397*4882a593Smuzhiyun return true;
2398*4882a593Smuzhiyun }
2399*4882a593Smuzhiyun
2400*4882a593Smuzhiyun /*===========================================================================
2401*4882a593Smuzhiyun METHOD:
2402*4882a593Smuzhiyun PopFromURBList (Public Method)
2403*4882a593Smuzhiyun
2404*4882a593Smuzhiyun DESCRIPTION:
2405*4882a593Smuzhiyun Remove URB from this client's URB list
2406*4882a593Smuzhiyun
2407*4882a593Smuzhiyun Caller MUST have lock on mClientMemLock
2408*4882a593Smuzhiyun
2409*4882a593Smuzhiyun PARAMETERS:
2410*4882a593Smuzhiyun pDev [ I ] - Device specific memory
2411*4882a593Smuzhiyun clientID [ I ] - Requester's client ID
2412*4882a593Smuzhiyun
2413*4882a593Smuzhiyun RETURN VALUE:
2414*4882a593Smuzhiyun struct urb - Pointer to requested client's URB
2415*4882a593Smuzhiyun NULL for error
2416*4882a593Smuzhiyun ===========================================================================*/
2417*4882a593Smuzhiyun static struct urb * PopFromURBList(
2418*4882a593Smuzhiyun sGobiUSBNet * pDev,
2419*4882a593Smuzhiyun u16 clientID )
2420*4882a593Smuzhiyun {
2421*4882a593Smuzhiyun sClientMemList * pClientMem;
2422*4882a593Smuzhiyun sURBList * pDelURBList;
2423*4882a593Smuzhiyun struct urb * pURB;
2424*4882a593Smuzhiyun
2425*4882a593Smuzhiyun #ifdef CONFIG_SMP
2426*4882a593Smuzhiyun // Verify Lock
2427*4882a593Smuzhiyun if (spin_is_locked( &pDev->mQMIDev.mClientMemLock ) == 0)
2428*4882a593Smuzhiyun {
2429*4882a593Smuzhiyun DBG( "unlocked\n" );
2430*4882a593Smuzhiyun BUG();
2431*4882a593Smuzhiyun }
2432*4882a593Smuzhiyun #endif
2433*4882a593Smuzhiyun
2434*4882a593Smuzhiyun // Get this client's memory location
2435*4882a593Smuzhiyun pClientMem = FindClientMem( pDev, clientID );
2436*4882a593Smuzhiyun if (pClientMem == NULL)
2437*4882a593Smuzhiyun {
2438*4882a593Smuzhiyun DBG( "Could not find this client's memory 0x%04X\n", clientID );
2439*4882a593Smuzhiyun return NULL;
2440*4882a593Smuzhiyun }
2441*4882a593Smuzhiyun
2442*4882a593Smuzhiyun // Remove from list
2443*4882a593Smuzhiyun if (pClientMem->mpURBList != NULL)
2444*4882a593Smuzhiyun {
2445*4882a593Smuzhiyun pDelURBList = pClientMem->mpURBList;
2446*4882a593Smuzhiyun pClientMem->mpURBList = pClientMem->mpURBList->mpNext;
2447*4882a593Smuzhiyun
2448*4882a593Smuzhiyun // Copy to output
2449*4882a593Smuzhiyun pURB = pDelURBList->mpURB;
2450*4882a593Smuzhiyun
2451*4882a593Smuzhiyun // Delete memory
2452*4882a593Smuzhiyun kfree( pDelURBList );
2453*4882a593Smuzhiyun
2454*4882a593Smuzhiyun return pURB;
2455*4882a593Smuzhiyun }
2456*4882a593Smuzhiyun else
2457*4882a593Smuzhiyun {
2458*4882a593Smuzhiyun DBG( "No URB's to pop\n" );
2459*4882a593Smuzhiyun
2460*4882a593Smuzhiyun return NULL;
2461*4882a593Smuzhiyun }
2462*4882a593Smuzhiyun }
2463*4882a593Smuzhiyun
2464*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION( 3,19,0 ))
2465*4882a593Smuzhiyun #ifndef f_dentry
2466*4882a593Smuzhiyun #define f_dentry f_path.dentry
2467*4882a593Smuzhiyun #endif
2468*4882a593Smuzhiyun #endif
2469*4882a593Smuzhiyun
2470*4882a593Smuzhiyun /*=========================================================================*/
2471*4882a593Smuzhiyun // Internal userspace wrappers
2472*4882a593Smuzhiyun /*=========================================================================*/
2473*4882a593Smuzhiyun
2474*4882a593Smuzhiyun /*===========================================================================
2475*4882a593Smuzhiyun METHOD:
2476*4882a593Smuzhiyun UserspaceunlockedIOCTL (Public Method)
2477*4882a593Smuzhiyun
2478*4882a593Smuzhiyun DESCRIPTION:
2479*4882a593Smuzhiyun Internal wrapper for Userspace IOCTL interface
2480*4882a593Smuzhiyun
2481*4882a593Smuzhiyun PARAMETERS
2482*4882a593Smuzhiyun pFilp [ I ] - userspace file descriptor
2483*4882a593Smuzhiyun cmd [ I ] - IOCTL command
2484*4882a593Smuzhiyun arg [ I ] - IOCTL argument
2485*4882a593Smuzhiyun
2486*4882a593Smuzhiyun RETURN VALUE:
2487*4882a593Smuzhiyun long - 0 for success
2488*4882a593Smuzhiyun Negative errno for failure
2489*4882a593Smuzhiyun ===========================================================================*/
2490*4882a593Smuzhiyun static long UserspaceunlockedIOCTL(
2491*4882a593Smuzhiyun struct file * pFilp,
2492*4882a593Smuzhiyun unsigned int cmd,
2493*4882a593Smuzhiyun unsigned long arg )
2494*4882a593Smuzhiyun {
2495*4882a593Smuzhiyun int result;
2496*4882a593Smuzhiyun u32 devVIDPID;
2497*4882a593Smuzhiyun
2498*4882a593Smuzhiyun sQMIFilpStorage * pFilpData = (sQMIFilpStorage *)pFilp->private_data;
2499*4882a593Smuzhiyun
2500*4882a593Smuzhiyun if (pFilpData == NULL)
2501*4882a593Smuzhiyun {
2502*4882a593Smuzhiyun DBG( "Bad file data\n" );
2503*4882a593Smuzhiyun return -EBADF;
2504*4882a593Smuzhiyun }
2505*4882a593Smuzhiyun
2506*4882a593Smuzhiyun if (IsDeviceValid( pFilpData->mpDev ) == false)
2507*4882a593Smuzhiyun {
2508*4882a593Smuzhiyun DBG( "Invalid device! Updating f_ops\n" );
2509*4882a593Smuzhiyun pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
2510*4882a593Smuzhiyun return -ENXIO;
2511*4882a593Smuzhiyun }
2512*4882a593Smuzhiyun
2513*4882a593Smuzhiyun switch (cmd)
2514*4882a593Smuzhiyun {
2515*4882a593Smuzhiyun case IOCTL_QMI_GET_SERVICE_FILE:
2516*4882a593Smuzhiyun DBG( "Setting up QMI for service %lu\n", arg );
2517*4882a593Smuzhiyun if ((u8)arg == 0)
2518*4882a593Smuzhiyun {
2519*4882a593Smuzhiyun DBG( "Cannot use QMICTL from userspace\n" );
2520*4882a593Smuzhiyun return -EINVAL;
2521*4882a593Smuzhiyun }
2522*4882a593Smuzhiyun
2523*4882a593Smuzhiyun // Connection is already setup
2524*4882a593Smuzhiyun if (pFilpData->mClientID != (u16)-1)
2525*4882a593Smuzhiyun {
2526*4882a593Smuzhiyun DBG( "Close the current connection before opening a new one\n" );
2527*4882a593Smuzhiyun return -EBADR;
2528*4882a593Smuzhiyun }
2529*4882a593Smuzhiyun
2530*4882a593Smuzhiyun result = GetClientID( pFilpData->mpDev, (u8)arg );
2531*4882a593Smuzhiyun // it seems QMIWDA only allow one client, if the last quectel-CM donot realese it (killed by SIGKILL).
2532*4882a593Smuzhiyun // can force release it at here
2533*4882a593Smuzhiyun #if 1
2534*4882a593Smuzhiyun if (result < 0 && (u8)arg == QMIWDA)
2535*4882a593Smuzhiyun {
2536*4882a593Smuzhiyun ReleaseClientID( pFilpData->mpDev, QMIWDA | (1 << 8) );
2537*4882a593Smuzhiyun result = GetClientID( pFilpData->mpDev, (u8)arg );
2538*4882a593Smuzhiyun }
2539*4882a593Smuzhiyun #endif
2540*4882a593Smuzhiyun if (result < 0)
2541*4882a593Smuzhiyun {
2542*4882a593Smuzhiyun return result;
2543*4882a593Smuzhiyun }
2544*4882a593Smuzhiyun pFilpData->mClientID = (u16)result;
2545*4882a593Smuzhiyun DBG("pFilpData->mClientID = 0x%x\n", pFilpData->mClientID );
2546*4882a593Smuzhiyun return 0;
2547*4882a593Smuzhiyun break;
2548*4882a593Smuzhiyun
2549*4882a593Smuzhiyun
2550*4882a593Smuzhiyun case IOCTL_QMI_GET_DEVICE_VIDPID:
2551*4882a593Smuzhiyun if (arg == 0)
2552*4882a593Smuzhiyun {
2553*4882a593Smuzhiyun DBG( "Bad VIDPID buffer\n" );
2554*4882a593Smuzhiyun return -EINVAL;
2555*4882a593Smuzhiyun }
2556*4882a593Smuzhiyun
2557*4882a593Smuzhiyun // Extra verification
2558*4882a593Smuzhiyun if (pFilpData->mpDev->mpNetDev == 0)
2559*4882a593Smuzhiyun {
2560*4882a593Smuzhiyun DBG( "Bad mpNetDev\n" );
2561*4882a593Smuzhiyun return -ENOMEM;
2562*4882a593Smuzhiyun }
2563*4882a593Smuzhiyun if (pFilpData->mpDev->mpNetDev->udev == 0)
2564*4882a593Smuzhiyun {
2565*4882a593Smuzhiyun DBG( "Bad udev\n" );
2566*4882a593Smuzhiyun return -ENOMEM;
2567*4882a593Smuzhiyun }
2568*4882a593Smuzhiyun
2569*4882a593Smuzhiyun devVIDPID = ((le16_to_cpu( pFilpData->mpDev->mpNetDev->udev->descriptor.idVendor ) << 16)
2570*4882a593Smuzhiyun + le16_to_cpu( pFilpData->mpDev->mpNetDev->udev->descriptor.idProduct ) );
2571*4882a593Smuzhiyun
2572*4882a593Smuzhiyun result = copy_to_user( (unsigned int *)arg, &devVIDPID, 4 );
2573*4882a593Smuzhiyun if (result != 0)
2574*4882a593Smuzhiyun {
2575*4882a593Smuzhiyun DBG( "Copy to userspace failure %d\n", result );
2576*4882a593Smuzhiyun }
2577*4882a593Smuzhiyun
2578*4882a593Smuzhiyun return result;
2579*4882a593Smuzhiyun
2580*4882a593Smuzhiyun break;
2581*4882a593Smuzhiyun
2582*4882a593Smuzhiyun case IOCTL_QMI_GET_DEVICE_MEID:
2583*4882a593Smuzhiyun if (arg == 0)
2584*4882a593Smuzhiyun {
2585*4882a593Smuzhiyun DBG( "Bad MEID buffer\n" );
2586*4882a593Smuzhiyun return -EINVAL;
2587*4882a593Smuzhiyun }
2588*4882a593Smuzhiyun
2589*4882a593Smuzhiyun result = copy_to_user( (unsigned int *)arg, &pFilpData->mpDev->mMEID[0], 14 );
2590*4882a593Smuzhiyun if (result != 0)
2591*4882a593Smuzhiyun {
2592*4882a593Smuzhiyun DBG( "Copy to userspace failure %d\n", result );
2593*4882a593Smuzhiyun }
2594*4882a593Smuzhiyun
2595*4882a593Smuzhiyun return result;
2596*4882a593Smuzhiyun
2597*4882a593Smuzhiyun break;
2598*4882a593Smuzhiyun
2599*4882a593Smuzhiyun default:
2600*4882a593Smuzhiyun return -EBADRQC;
2601*4882a593Smuzhiyun }
2602*4882a593Smuzhiyun }
2603*4882a593Smuzhiyun
2604*4882a593Smuzhiyun /*=========================================================================*/
2605*4882a593Smuzhiyun // Userspace wrappers
2606*4882a593Smuzhiyun /*=========================================================================*/
2607*4882a593Smuzhiyun
2608*4882a593Smuzhiyun /*===========================================================================
2609*4882a593Smuzhiyun METHOD:
2610*4882a593Smuzhiyun UserspaceOpen (Public Method)
2611*4882a593Smuzhiyun
2612*4882a593Smuzhiyun DESCRIPTION:
2613*4882a593Smuzhiyun Userspace open
2614*4882a593Smuzhiyun IOCTL must be called before reads or writes
2615*4882a593Smuzhiyun
2616*4882a593Smuzhiyun PARAMETERS
2617*4882a593Smuzhiyun pInode [ I ] - kernel file descriptor
2618*4882a593Smuzhiyun pFilp [ I ] - userspace file descriptor
2619*4882a593Smuzhiyun
2620*4882a593Smuzhiyun RETURN VALUE:
2621*4882a593Smuzhiyun int - 0 for success
2622*4882a593Smuzhiyun Negative errno for failure
2623*4882a593Smuzhiyun ===========================================================================*/
2624*4882a593Smuzhiyun static int UserspaceOpen(
2625*4882a593Smuzhiyun struct inode * pInode,
2626*4882a593Smuzhiyun struct file * pFilp )
2627*4882a593Smuzhiyun {
2628*4882a593Smuzhiyun sQMIFilpStorage * pFilpData;
2629*4882a593Smuzhiyun
2630*4882a593Smuzhiyun // Optain device pointer from pInode
2631*4882a593Smuzhiyun sQMIDev * pQMIDev = container_of( pInode->i_cdev,
2632*4882a593Smuzhiyun sQMIDev,
2633*4882a593Smuzhiyun mCdev );
2634*4882a593Smuzhiyun sGobiUSBNet * pDev = container_of( pQMIDev,
2635*4882a593Smuzhiyun sGobiUSBNet,
2636*4882a593Smuzhiyun mQMIDev );
2637*4882a593Smuzhiyun
2638*4882a593Smuzhiyun if (pDev->mbMdm9x07)
2639*4882a593Smuzhiyun {
2640*4882a593Smuzhiyun atomic_inc(&pDev->refcount);
2641*4882a593Smuzhiyun if (!pDev->mbQMIReady) {
2642*4882a593Smuzhiyun if (wait_for_completion_interruptible_timeout(&pDev->mQMIReadyCompletion, 15*HZ) <= 0) {
2643*4882a593Smuzhiyun if (atomic_dec_and_test(&pDev->refcount)) {
2644*4882a593Smuzhiyun kfree( pDev );
2645*4882a593Smuzhiyun }
2646*4882a593Smuzhiyun return -ETIMEDOUT;
2647*4882a593Smuzhiyun }
2648*4882a593Smuzhiyun }
2649*4882a593Smuzhiyun atomic_dec(&pDev->refcount);
2650*4882a593Smuzhiyun }
2651*4882a593Smuzhiyun
2652*4882a593Smuzhiyun if (IsDeviceValid( pDev ) == false)
2653*4882a593Smuzhiyun {
2654*4882a593Smuzhiyun DBG( "Invalid device\n" );
2655*4882a593Smuzhiyun return -ENXIO;
2656*4882a593Smuzhiyun }
2657*4882a593Smuzhiyun
2658*4882a593Smuzhiyun // Setup data in pFilp->private_data
2659*4882a593Smuzhiyun pFilp->private_data = kmalloc( sizeof( sQMIFilpStorage ), GFP_KERNEL );
2660*4882a593Smuzhiyun if (pFilp->private_data == NULL)
2661*4882a593Smuzhiyun {
2662*4882a593Smuzhiyun DBG( "Mem error\n" );
2663*4882a593Smuzhiyun return -ENOMEM;
2664*4882a593Smuzhiyun }
2665*4882a593Smuzhiyun
2666*4882a593Smuzhiyun pFilpData = (sQMIFilpStorage *)pFilp->private_data;
2667*4882a593Smuzhiyun pFilpData->mClientID = (u16)-1;
2668*4882a593Smuzhiyun pFilpData->mpDev = pDev;
2669*4882a593Smuzhiyun atomic_inc(&pFilpData->mpDev->refcount);
2670*4882a593Smuzhiyun
2671*4882a593Smuzhiyun return 0;
2672*4882a593Smuzhiyun }
2673*4882a593Smuzhiyun
2674*4882a593Smuzhiyun /*===========================================================================
2675*4882a593Smuzhiyun METHOD:
2676*4882a593Smuzhiyun UserspaceIOCTL (Public Method)
2677*4882a593Smuzhiyun
2678*4882a593Smuzhiyun DESCRIPTION:
2679*4882a593Smuzhiyun Userspace IOCTL functions
2680*4882a593Smuzhiyun
2681*4882a593Smuzhiyun PARAMETERS
2682*4882a593Smuzhiyun pUnusedInode [ I ] - (unused) kernel file descriptor
2683*4882a593Smuzhiyun pFilp [ I ] - userspace file descriptor
2684*4882a593Smuzhiyun cmd [ I ] - IOCTL command
2685*4882a593Smuzhiyun arg [ I ] - IOCTL argument
2686*4882a593Smuzhiyun
2687*4882a593Smuzhiyun RETURN VALUE:
2688*4882a593Smuzhiyun int - 0 for success
2689*4882a593Smuzhiyun Negative errno for failure
2690*4882a593Smuzhiyun ===========================================================================*/
2691*4882a593Smuzhiyun #if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,36 ))
2692*4882a593Smuzhiyun static int UserspaceIOCTL(
2693*4882a593Smuzhiyun struct inode * pUnusedInode,
2694*4882a593Smuzhiyun struct file * pFilp,
2695*4882a593Smuzhiyun unsigned int cmd,
2696*4882a593Smuzhiyun unsigned long arg )
2697*4882a593Smuzhiyun {
2698*4882a593Smuzhiyun // call the internal wrapper function
2699*4882a593Smuzhiyun return (int)UserspaceunlockedIOCTL( pFilp, cmd, arg );
2700*4882a593Smuzhiyun }
2701*4882a593Smuzhiyun #endif
2702*4882a593Smuzhiyun
2703*4882a593Smuzhiyun #ifdef quectel_no_for_each_process
2704*4882a593Smuzhiyun static int UserspaceClose(
2705*4882a593Smuzhiyun struct inode * pInode,
2706*4882a593Smuzhiyun struct file * pFilp )
2707*4882a593Smuzhiyun {
2708*4882a593Smuzhiyun sQMIFilpStorage * pFilpData = (sQMIFilpStorage *)pFilp->private_data;
2709*4882a593Smuzhiyun
2710*4882a593Smuzhiyun if (pFilpData == NULL)
2711*4882a593Smuzhiyun {
2712*4882a593Smuzhiyun DBG( "bad file data\n" );
2713*4882a593Smuzhiyun return -EBADF;
2714*4882a593Smuzhiyun }
2715*4882a593Smuzhiyun
2716*4882a593Smuzhiyun atomic_dec(&pFilpData->mpDev->refcount);
2717*4882a593Smuzhiyun
2718*4882a593Smuzhiyun if (IsDeviceValid( pFilpData->mpDev ) == false)
2719*4882a593Smuzhiyun {
2720*4882a593Smuzhiyun return -ENXIO;
2721*4882a593Smuzhiyun }
2722*4882a593Smuzhiyun
2723*4882a593Smuzhiyun DBG( "0x%04X\n", pFilpData->mClientID );
2724*4882a593Smuzhiyun
2725*4882a593Smuzhiyun // Disable pFilpData so they can't keep sending read or write
2726*4882a593Smuzhiyun // should this function hang
2727*4882a593Smuzhiyun // Note: memory pointer is still saved in pFilpData to be deleted later
2728*4882a593Smuzhiyun pFilp->private_data = NULL;
2729*4882a593Smuzhiyun
2730*4882a593Smuzhiyun if (pFilpData->mClientID != (u16)-1)
2731*4882a593Smuzhiyun {
2732*4882a593Smuzhiyun if (pFilpData->mpDev->mbDeregisterQMIDevice)
2733*4882a593Smuzhiyun pFilpData->mClientID = (u16)-1; //DeregisterQMIDevice() will release this ClientID
2734*4882a593Smuzhiyun else
2735*4882a593Smuzhiyun ReleaseClientID( pFilpData->mpDev,
2736*4882a593Smuzhiyun pFilpData->mClientID );
2737*4882a593Smuzhiyun }
2738*4882a593Smuzhiyun
2739*4882a593Smuzhiyun kfree( pFilpData );
2740*4882a593Smuzhiyun return 0;
2741*4882a593Smuzhiyun }
2742*4882a593Smuzhiyun #else
2743*4882a593Smuzhiyun /*===========================================================================
2744*4882a593Smuzhiyun METHOD:
2745*4882a593Smuzhiyun UserspaceClose (Public Method)
2746*4882a593Smuzhiyun
2747*4882a593Smuzhiyun DESCRIPTION:
2748*4882a593Smuzhiyun Userspace close
2749*4882a593Smuzhiyun Release client ID and free memory
2750*4882a593Smuzhiyun
2751*4882a593Smuzhiyun PARAMETERS
2752*4882a593Smuzhiyun pFilp [ I ] - userspace file descriptor
2753*4882a593Smuzhiyun unusedFileTable [ I ] - (unused) file table
2754*4882a593Smuzhiyun
2755*4882a593Smuzhiyun RETURN VALUE:
2756*4882a593Smuzhiyun int - 0 for success
2757*4882a593Smuzhiyun Negative errno for failure
2758*4882a593Smuzhiyun ===========================================================================*/
2759*4882a593Smuzhiyun #if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,14 ))
2760*4882a593Smuzhiyun int UserspaceClose(
2761*4882a593Smuzhiyun struct file * pFilp,
2762*4882a593Smuzhiyun fl_owner_t unusedFileTable )
2763*4882a593Smuzhiyun #else
2764*4882a593Smuzhiyun int UserspaceClose( struct file * pFilp )
2765*4882a593Smuzhiyun #endif
2766*4882a593Smuzhiyun {
2767*4882a593Smuzhiyun sQMIFilpStorage * pFilpData = (sQMIFilpStorage *)pFilp->private_data;
2768*4882a593Smuzhiyun struct task_struct * pEachTask;
2769*4882a593Smuzhiyun struct fdtable * pFDT;
2770*4882a593Smuzhiyun int count = 0;
2771*4882a593Smuzhiyun int used = 0;
2772*4882a593Smuzhiyun unsigned long flags;
2773*4882a593Smuzhiyun
2774*4882a593Smuzhiyun if (pFilpData == NULL)
2775*4882a593Smuzhiyun {
2776*4882a593Smuzhiyun DBG( "bad file data\n" );
2777*4882a593Smuzhiyun return -EBADF;
2778*4882a593Smuzhiyun }
2779*4882a593Smuzhiyun
2780*4882a593Smuzhiyun // Fallthough. If f_count == 1 no need to do more checks
2781*4882a593Smuzhiyun #if (LINUX_VERSION_CODE <= KERNEL_VERSION( 2,6,24 ))
2782*4882a593Smuzhiyun if (atomic_read( &pFilp->f_count ) != 1)
2783*4882a593Smuzhiyun #else
2784*4882a593Smuzhiyun if (atomic_long_read( &pFilp->f_count ) != 1)
2785*4882a593Smuzhiyun #endif
2786*4882a593Smuzhiyun {
2787*4882a593Smuzhiyun rcu_read_lock();
2788*4882a593Smuzhiyun for_each_process( pEachTask )
2789*4882a593Smuzhiyun {
2790*4882a593Smuzhiyun task_lock(pEachTask);
2791*4882a593Smuzhiyun if (pEachTask == NULL || pEachTask->files == NULL)
2792*4882a593Smuzhiyun {
2793*4882a593Smuzhiyun // Some tasks may not have files (e.g. Xsession)
2794*4882a593Smuzhiyun task_unlock(pEachTask);
2795*4882a593Smuzhiyun continue;
2796*4882a593Smuzhiyun }
2797*4882a593Smuzhiyun spin_lock_irqsave( &pEachTask->files->file_lock, flags );
2798*4882a593Smuzhiyun task_unlock(pEachTask); //kernel/exit.c:do_exit() -> fs/file.c:exit_files()
2799*4882a593Smuzhiyun pFDT = files_fdtable( pEachTask->files );
2800*4882a593Smuzhiyun for (count = 0; count < pFDT->max_fds; count++)
2801*4882a593Smuzhiyun {
2802*4882a593Smuzhiyun // Before this function was called, this file was removed
2803*4882a593Smuzhiyun // from our task's file table so if we find it in a file
2804*4882a593Smuzhiyun // table then it is being used by another task
2805*4882a593Smuzhiyun if (pFDT->fd[count] == pFilp)
2806*4882a593Smuzhiyun {
2807*4882a593Smuzhiyun used++;
2808*4882a593Smuzhiyun break;
2809*4882a593Smuzhiyun }
2810*4882a593Smuzhiyun }
2811*4882a593Smuzhiyun spin_unlock_irqrestore( &pEachTask->files->file_lock, flags );
2812*4882a593Smuzhiyun }
2813*4882a593Smuzhiyun rcu_read_unlock();
2814*4882a593Smuzhiyun
2815*4882a593Smuzhiyun if (used > 0)
2816*4882a593Smuzhiyun {
2817*4882a593Smuzhiyun DBG( "not closing, as this FD is open by %d other process\n", used );
2818*4882a593Smuzhiyun return 0;
2819*4882a593Smuzhiyun }
2820*4882a593Smuzhiyun }
2821*4882a593Smuzhiyun
2822*4882a593Smuzhiyun if (IsDeviceValid( pFilpData->mpDev ) == false)
2823*4882a593Smuzhiyun {
2824*4882a593Smuzhiyun DBG( "Invalid device! Updating f_ops\n" );
2825*4882a593Smuzhiyun pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
2826*4882a593Smuzhiyun return -ENXIO;
2827*4882a593Smuzhiyun }
2828*4882a593Smuzhiyun
2829*4882a593Smuzhiyun DBG( "0x%04X\n", pFilpData->mClientID );
2830*4882a593Smuzhiyun
2831*4882a593Smuzhiyun // Disable pFilpData so they can't keep sending read or write
2832*4882a593Smuzhiyun // should this function hang
2833*4882a593Smuzhiyun // Note: memory pointer is still saved in pFilpData to be deleted later
2834*4882a593Smuzhiyun pFilp->private_data = NULL;
2835*4882a593Smuzhiyun
2836*4882a593Smuzhiyun if (pFilpData->mClientID != (u16)-1)
2837*4882a593Smuzhiyun {
2838*4882a593Smuzhiyun if (pFilpData->mpDev->mbDeregisterQMIDevice)
2839*4882a593Smuzhiyun pFilpData->mClientID = (u16)-1; //DeregisterQMIDevice() will release this ClientID
2840*4882a593Smuzhiyun else
2841*4882a593Smuzhiyun ReleaseClientID( pFilpData->mpDev,
2842*4882a593Smuzhiyun pFilpData->mClientID );
2843*4882a593Smuzhiyun }
2844*4882a593Smuzhiyun atomic_dec(&pFilpData->mpDev->refcount);
2845*4882a593Smuzhiyun
2846*4882a593Smuzhiyun kfree( pFilpData );
2847*4882a593Smuzhiyun return 0;
2848*4882a593Smuzhiyun }
2849*4882a593Smuzhiyun #endif
2850*4882a593Smuzhiyun
2851*4882a593Smuzhiyun /*===========================================================================
2852*4882a593Smuzhiyun METHOD:
2853*4882a593Smuzhiyun UserspaceRead (Public Method)
2854*4882a593Smuzhiyun
2855*4882a593Smuzhiyun DESCRIPTION:
2856*4882a593Smuzhiyun Userspace read (synchronous)
2857*4882a593Smuzhiyun
2858*4882a593Smuzhiyun PARAMETERS
2859*4882a593Smuzhiyun pFilp [ I ] - userspace file descriptor
2860*4882a593Smuzhiyun pBuf [ I ] - read buffer
2861*4882a593Smuzhiyun size [ I ] - size of read buffer
2862*4882a593Smuzhiyun pUnusedFpos [ I ] - (unused) file position
2863*4882a593Smuzhiyun
2864*4882a593Smuzhiyun RETURN VALUE:
2865*4882a593Smuzhiyun ssize_t - Number of bytes read for success
2866*4882a593Smuzhiyun Negative errno for failure
2867*4882a593Smuzhiyun ===========================================================================*/
2868*4882a593Smuzhiyun static ssize_t UserspaceRead(
2869*4882a593Smuzhiyun struct file * pFilp,
2870*4882a593Smuzhiyun char __user * pBuf,
2871*4882a593Smuzhiyun size_t size,
2872*4882a593Smuzhiyun loff_t * pUnusedFpos )
2873*4882a593Smuzhiyun {
2874*4882a593Smuzhiyun int result;
2875*4882a593Smuzhiyun void * pReadData = NULL;
2876*4882a593Smuzhiyun void * pSmallReadData;
2877*4882a593Smuzhiyun sQMIFilpStorage * pFilpData = (sQMIFilpStorage *)pFilp->private_data;
2878*4882a593Smuzhiyun
2879*4882a593Smuzhiyun if (pFilpData == NULL)
2880*4882a593Smuzhiyun {
2881*4882a593Smuzhiyun DBG( "Bad file data\n" );
2882*4882a593Smuzhiyun return -EBADF;
2883*4882a593Smuzhiyun }
2884*4882a593Smuzhiyun
2885*4882a593Smuzhiyun if (IsDeviceValid( pFilpData->mpDev ) == false)
2886*4882a593Smuzhiyun {
2887*4882a593Smuzhiyun DBG( "Invalid device! Updating f_ops\n" );
2888*4882a593Smuzhiyun pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
2889*4882a593Smuzhiyun return -ENXIO;
2890*4882a593Smuzhiyun }
2891*4882a593Smuzhiyun
2892*4882a593Smuzhiyun if (pFilpData->mClientID == (u16)-1)
2893*4882a593Smuzhiyun {
2894*4882a593Smuzhiyun DBG( "Client ID must be set before reading 0x%04X\n",
2895*4882a593Smuzhiyun pFilpData->mClientID );
2896*4882a593Smuzhiyun return -EBADR;
2897*4882a593Smuzhiyun }
2898*4882a593Smuzhiyun
2899*4882a593Smuzhiyun // Perform synchronous read
2900*4882a593Smuzhiyun result = ReadSync( pFilpData->mpDev,
2901*4882a593Smuzhiyun &pReadData,
2902*4882a593Smuzhiyun pFilpData->mClientID,
2903*4882a593Smuzhiyun 0 );
2904*4882a593Smuzhiyun if (result <= 0)
2905*4882a593Smuzhiyun {
2906*4882a593Smuzhiyun return result;
2907*4882a593Smuzhiyun }
2908*4882a593Smuzhiyun
2909*4882a593Smuzhiyun // Discard QMUX header
2910*4882a593Smuzhiyun result -= QMUXHeaderSize();
2911*4882a593Smuzhiyun pSmallReadData = pReadData + QMUXHeaderSize();
2912*4882a593Smuzhiyun
2913*4882a593Smuzhiyun if (result > size)
2914*4882a593Smuzhiyun {
2915*4882a593Smuzhiyun DBG( "Read data is too large for amount user has requested\n" );
2916*4882a593Smuzhiyun kfree( pReadData );
2917*4882a593Smuzhiyun return -EOVERFLOW;
2918*4882a593Smuzhiyun }
2919*4882a593Smuzhiyun
2920*4882a593Smuzhiyun DBG( "pBuf = 0x%p pSmallReadData = 0x%p, result = %d",
2921*4882a593Smuzhiyun pBuf, pSmallReadData, result );
2922*4882a593Smuzhiyun
2923*4882a593Smuzhiyun if (copy_to_user( pBuf, pSmallReadData, result ) != 0)
2924*4882a593Smuzhiyun {
2925*4882a593Smuzhiyun DBG( "Error copying read data to user\n" );
2926*4882a593Smuzhiyun result = -EFAULT;
2927*4882a593Smuzhiyun }
2928*4882a593Smuzhiyun
2929*4882a593Smuzhiyun // Reader is responsible for freeing read buffer
2930*4882a593Smuzhiyun kfree( pReadData );
2931*4882a593Smuzhiyun
2932*4882a593Smuzhiyun return result;
2933*4882a593Smuzhiyun }
2934*4882a593Smuzhiyun
2935*4882a593Smuzhiyun /*===========================================================================
2936*4882a593Smuzhiyun METHOD:
2937*4882a593Smuzhiyun UserspaceWrite (Public Method)
2938*4882a593Smuzhiyun
2939*4882a593Smuzhiyun DESCRIPTION:
2940*4882a593Smuzhiyun Userspace write (synchronous)
2941*4882a593Smuzhiyun
2942*4882a593Smuzhiyun PARAMETERS
2943*4882a593Smuzhiyun pFilp [ I ] - userspace file descriptor
2944*4882a593Smuzhiyun pBuf [ I ] - write buffer
2945*4882a593Smuzhiyun size [ I ] - size of write buffer
2946*4882a593Smuzhiyun pUnusedFpos [ I ] - (unused) file position
2947*4882a593Smuzhiyun
2948*4882a593Smuzhiyun RETURN VALUE:
2949*4882a593Smuzhiyun ssize_t - Number of bytes read for success
2950*4882a593Smuzhiyun Negative errno for failure
2951*4882a593Smuzhiyun ===========================================================================*/
2952*4882a593Smuzhiyun static ssize_t UserspaceWrite(
2953*4882a593Smuzhiyun struct file * pFilp,
2954*4882a593Smuzhiyun const char __user * pBuf,
2955*4882a593Smuzhiyun size_t size,
2956*4882a593Smuzhiyun loff_t * pUnusedFpos )
2957*4882a593Smuzhiyun {
2958*4882a593Smuzhiyun int status;
2959*4882a593Smuzhiyun void * pWriteBuffer;
2960*4882a593Smuzhiyun sQMIFilpStorage * pFilpData = (sQMIFilpStorage *)pFilp->private_data;
2961*4882a593Smuzhiyun
2962*4882a593Smuzhiyun if (pFilpData == NULL)
2963*4882a593Smuzhiyun {
2964*4882a593Smuzhiyun DBG( "Bad file data\n" );
2965*4882a593Smuzhiyun return -EBADF;
2966*4882a593Smuzhiyun }
2967*4882a593Smuzhiyun
2968*4882a593Smuzhiyun if (IsDeviceValid( pFilpData->mpDev ) == false)
2969*4882a593Smuzhiyun {
2970*4882a593Smuzhiyun DBG( "Invalid device! Updating f_ops\n" );
2971*4882a593Smuzhiyun pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
2972*4882a593Smuzhiyun return -ENXIO;
2973*4882a593Smuzhiyun }
2974*4882a593Smuzhiyun
2975*4882a593Smuzhiyun if (pFilpData->mClientID == (u16)-1)
2976*4882a593Smuzhiyun {
2977*4882a593Smuzhiyun DBG( "Client ID must be set before writing 0x%04X\n",
2978*4882a593Smuzhiyun pFilpData->mClientID );
2979*4882a593Smuzhiyun return -EBADR;
2980*4882a593Smuzhiyun }
2981*4882a593Smuzhiyun
2982*4882a593Smuzhiyun // Copy data from user to kernel space
2983*4882a593Smuzhiyun pWriteBuffer = kmalloc( size + QMUXHeaderSize(), GFP_KERNEL );
2984*4882a593Smuzhiyun if (pWriteBuffer == NULL)
2985*4882a593Smuzhiyun {
2986*4882a593Smuzhiyun return -ENOMEM;
2987*4882a593Smuzhiyun }
2988*4882a593Smuzhiyun status = copy_from_user( pWriteBuffer + QMUXHeaderSize(), pBuf, size );
2989*4882a593Smuzhiyun if (status != 0)
2990*4882a593Smuzhiyun {
2991*4882a593Smuzhiyun DBG( "Unable to copy data from userspace %d\n", status );
2992*4882a593Smuzhiyun kfree( pWriteBuffer );
2993*4882a593Smuzhiyun return status;
2994*4882a593Smuzhiyun }
2995*4882a593Smuzhiyun
2996*4882a593Smuzhiyun status = WriteSync( pFilpData->mpDev,
2997*4882a593Smuzhiyun pWriteBuffer,
2998*4882a593Smuzhiyun size + QMUXHeaderSize(),
2999*4882a593Smuzhiyun pFilpData->mClientID );
3000*4882a593Smuzhiyun
3001*4882a593Smuzhiyun kfree( pWriteBuffer );
3002*4882a593Smuzhiyun
3003*4882a593Smuzhiyun // On success, return requested size, not full QMI reqest size
3004*4882a593Smuzhiyun if (status == size + QMUXHeaderSize())
3005*4882a593Smuzhiyun {
3006*4882a593Smuzhiyun return size;
3007*4882a593Smuzhiyun }
3008*4882a593Smuzhiyun else
3009*4882a593Smuzhiyun {
3010*4882a593Smuzhiyun return status;
3011*4882a593Smuzhiyun }
3012*4882a593Smuzhiyun }
3013*4882a593Smuzhiyun
3014*4882a593Smuzhiyun /*===========================================================================
3015*4882a593Smuzhiyun METHOD:
3016*4882a593Smuzhiyun UserspacePoll (Public Method)
3017*4882a593Smuzhiyun
3018*4882a593Smuzhiyun DESCRIPTION:
3019*4882a593Smuzhiyun Used to determine if read/write operations are possible without blocking
3020*4882a593Smuzhiyun
3021*4882a593Smuzhiyun PARAMETERS
3022*4882a593Smuzhiyun pFilp [ I ] - userspace file descriptor
3023*4882a593Smuzhiyun pPollTable [I/O] - Wait object to notify the kernel when data
3024*4882a593Smuzhiyun is ready
3025*4882a593Smuzhiyun
3026*4882a593Smuzhiyun RETURN VALUE:
3027*4882a593Smuzhiyun unsigned int - bitmask of what operations can be done immediately
3028*4882a593Smuzhiyun ===========================================================================*/
3029*4882a593Smuzhiyun static unsigned int UserspacePoll(
3030*4882a593Smuzhiyun struct file * pFilp,
3031*4882a593Smuzhiyun struct poll_table_struct * pPollTable )
3032*4882a593Smuzhiyun {
3033*4882a593Smuzhiyun sQMIFilpStorage * pFilpData = (sQMIFilpStorage *)pFilp->private_data;
3034*4882a593Smuzhiyun sClientMemList * pClientMem;
3035*4882a593Smuzhiyun unsigned long flags;
3036*4882a593Smuzhiyun
3037*4882a593Smuzhiyun // Always ready to write
3038*4882a593Smuzhiyun unsigned long status = POLLOUT | POLLWRNORM;
3039*4882a593Smuzhiyun
3040*4882a593Smuzhiyun if (pFilpData == NULL)
3041*4882a593Smuzhiyun {
3042*4882a593Smuzhiyun DBG( "Bad file data\n" );
3043*4882a593Smuzhiyun return POLLERR;
3044*4882a593Smuzhiyun }
3045*4882a593Smuzhiyun
3046*4882a593Smuzhiyun if (IsDeviceValid( pFilpData->mpDev ) == false)
3047*4882a593Smuzhiyun {
3048*4882a593Smuzhiyun DBG( "Invalid device! Updating f_ops\n" );
3049*4882a593Smuzhiyun pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
3050*4882a593Smuzhiyun return POLLERR;
3051*4882a593Smuzhiyun }
3052*4882a593Smuzhiyun
3053*4882a593Smuzhiyun if (pFilpData->mpDev->mbDeregisterQMIDevice)
3054*4882a593Smuzhiyun {
3055*4882a593Smuzhiyun DBG( "DeregisterQMIDevice ing\n" );
3056*4882a593Smuzhiyun return POLLHUP | POLLERR;
3057*4882a593Smuzhiyun }
3058*4882a593Smuzhiyun
3059*4882a593Smuzhiyun if (pFilpData->mClientID == (u16)-1)
3060*4882a593Smuzhiyun {
3061*4882a593Smuzhiyun DBG( "Client ID must be set before polling 0x%04X\n",
3062*4882a593Smuzhiyun pFilpData->mClientID );
3063*4882a593Smuzhiyun return POLLERR;
3064*4882a593Smuzhiyun }
3065*4882a593Smuzhiyun
3066*4882a593Smuzhiyun // Critical section
3067*4882a593Smuzhiyun spin_lock_irqsave( &pFilpData->mpDev->mQMIDev.mClientMemLock, flags );
3068*4882a593Smuzhiyun
3069*4882a593Smuzhiyun // Get this client's memory location
3070*4882a593Smuzhiyun pClientMem = FindClientMem( pFilpData->mpDev,
3071*4882a593Smuzhiyun pFilpData->mClientID );
3072*4882a593Smuzhiyun if (pClientMem == NULL)
3073*4882a593Smuzhiyun {
3074*4882a593Smuzhiyun DBG( "Could not find this client's memory 0x%04X\n",
3075*4882a593Smuzhiyun pFilpData->mClientID );
3076*4882a593Smuzhiyun
3077*4882a593Smuzhiyun spin_unlock_irqrestore( &pFilpData->mpDev->mQMIDev.mClientMemLock,
3078*4882a593Smuzhiyun flags );
3079*4882a593Smuzhiyun return POLLERR;
3080*4882a593Smuzhiyun }
3081*4882a593Smuzhiyun
3082*4882a593Smuzhiyun poll_wait( pFilp, &pClientMem->mWaitQueue, pPollTable );
3083*4882a593Smuzhiyun
3084*4882a593Smuzhiyun if (pClientMem->mpList != NULL)
3085*4882a593Smuzhiyun {
3086*4882a593Smuzhiyun status |= POLLIN | POLLRDNORM;
3087*4882a593Smuzhiyun }
3088*4882a593Smuzhiyun
3089*4882a593Smuzhiyun // End critical section
3090*4882a593Smuzhiyun spin_unlock_irqrestore( &pFilpData->mpDev->mQMIDev.mClientMemLock, flags );
3091*4882a593Smuzhiyun
3092*4882a593Smuzhiyun // Always ready to write
3093*4882a593Smuzhiyun return (status | POLLOUT | POLLWRNORM);
3094*4882a593Smuzhiyun }
3095*4882a593Smuzhiyun
3096*4882a593Smuzhiyun /*=========================================================================*/
3097*4882a593Smuzhiyun // Initializer and destructor
3098*4882a593Smuzhiyun /*=========================================================================*/
3099*4882a593Smuzhiyun static int QMICTLSyncProc(sGobiUSBNet *pDev)
3100*4882a593Smuzhiyun {
3101*4882a593Smuzhiyun void *pWriteBuffer;
3102*4882a593Smuzhiyun void *pReadBuffer;
3103*4882a593Smuzhiyun int result;
3104*4882a593Smuzhiyun u16 writeBufferSize;
3105*4882a593Smuzhiyun u16 readBufferSize;
3106*4882a593Smuzhiyun u8 transactionID;
3107*4882a593Smuzhiyun unsigned long flags;
3108*4882a593Smuzhiyun
3109*4882a593Smuzhiyun if (IsDeviceValid( pDev ) == false)
3110*4882a593Smuzhiyun {
3111*4882a593Smuzhiyun DBG( "Invalid device\n" );
3112*4882a593Smuzhiyun return -EFAULT;
3113*4882a593Smuzhiyun }
3114*4882a593Smuzhiyun
3115*4882a593Smuzhiyun writeBufferSize= QMICTLSyncReqSize();
3116*4882a593Smuzhiyun pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL );
3117*4882a593Smuzhiyun if (pWriteBuffer == NULL)
3118*4882a593Smuzhiyun {
3119*4882a593Smuzhiyun return -ENOMEM;
3120*4882a593Smuzhiyun }
3121*4882a593Smuzhiyun
3122*4882a593Smuzhiyun transactionID = QMIXactionIDGet(pDev);
3123*4882a593Smuzhiyun
3124*4882a593Smuzhiyun /* send a QMI_CTL_SYNC_REQ (0x0027) */
3125*4882a593Smuzhiyun result = QMICTLSyncReq( pWriteBuffer,
3126*4882a593Smuzhiyun writeBufferSize,
3127*4882a593Smuzhiyun transactionID );
3128*4882a593Smuzhiyun if (result < 0)
3129*4882a593Smuzhiyun {
3130*4882a593Smuzhiyun kfree( pWriteBuffer );
3131*4882a593Smuzhiyun return result;
3132*4882a593Smuzhiyun }
3133*4882a593Smuzhiyun
3134*4882a593Smuzhiyun result = WriteSync( pDev,
3135*4882a593Smuzhiyun pWriteBuffer,
3136*4882a593Smuzhiyun writeBufferSize,
3137*4882a593Smuzhiyun QMICTL );
3138*4882a593Smuzhiyun
3139*4882a593Smuzhiyun if (result < 0)
3140*4882a593Smuzhiyun {
3141*4882a593Smuzhiyun kfree( pWriteBuffer );
3142*4882a593Smuzhiyun return result;
3143*4882a593Smuzhiyun }
3144*4882a593Smuzhiyun
3145*4882a593Smuzhiyun // QMI CTL Sync Response
3146*4882a593Smuzhiyun result = ReadSync( pDev,
3147*4882a593Smuzhiyun &pReadBuffer,
3148*4882a593Smuzhiyun QMICTL,
3149*4882a593Smuzhiyun transactionID );
3150*4882a593Smuzhiyun if (result < 0)
3151*4882a593Smuzhiyun {
3152*4882a593Smuzhiyun return result;
3153*4882a593Smuzhiyun }
3154*4882a593Smuzhiyun
3155*4882a593Smuzhiyun result = QMICTLSyncResp( pReadBuffer,
3156*4882a593Smuzhiyun (u16)result );
3157*4882a593Smuzhiyun
3158*4882a593Smuzhiyun kfree( pReadBuffer );
3159*4882a593Smuzhiyun
3160*4882a593Smuzhiyun if (result < 0) /* need to re-sync */
3161*4882a593Smuzhiyun {
3162*4882a593Smuzhiyun DBG( "sync response error code %d\n", result );
3163*4882a593Smuzhiyun /* start timer and wait for the response */
3164*4882a593Smuzhiyun /* process response */
3165*4882a593Smuzhiyun return result;
3166*4882a593Smuzhiyun }
3167*4882a593Smuzhiyun
3168*4882a593Smuzhiyun #if 1 //free these ununsed qmi response, or when these transactionID re-used, they will be regarded as qmi response of the qmi request that have same transactionID
3169*4882a593Smuzhiyun // Enter critical section
3170*4882a593Smuzhiyun spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
3171*4882a593Smuzhiyun
3172*4882a593Smuzhiyun // Free any unread data
3173*4882a593Smuzhiyun while (PopFromReadMemList( pDev, QMICTL, 0, &pReadBuffer, &readBufferSize) == true) {
3174*4882a593Smuzhiyun kfree( pReadBuffer );
3175*4882a593Smuzhiyun }
3176*4882a593Smuzhiyun
3177*4882a593Smuzhiyun // End critical section
3178*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
3179*4882a593Smuzhiyun #endif
3180*4882a593Smuzhiyun
3181*4882a593Smuzhiyun // Success
3182*4882a593Smuzhiyun return 0;
3183*4882a593Smuzhiyun }
3184*4882a593Smuzhiyun
3185*4882a593Smuzhiyun static int qmi_sync_thread(void *data) {
3186*4882a593Smuzhiyun sGobiUSBNet * pDev = (sGobiUSBNet *)data;
3187*4882a593Smuzhiyun int result = 0;
3188*4882a593Smuzhiyun
3189*4882a593Smuzhiyun #if 1
3190*4882a593Smuzhiyun // Device is not ready for QMI connections right away
3191*4882a593Smuzhiyun // Wait up to 30 seconds before failing
3192*4882a593Smuzhiyun if (QMIReady( pDev, 30000 ) == false)
3193*4882a593Smuzhiyun {
3194*4882a593Smuzhiyun DBG( "Device unresponsive to QMI\n" );
3195*4882a593Smuzhiyun goto __qmi_sync_finished;
3196*4882a593Smuzhiyun }
3197*4882a593Smuzhiyun
3198*4882a593Smuzhiyun // Initiate QMI CTL Sync Procedure
3199*4882a593Smuzhiyun DBG( "Sending QMI CTL Sync Request\n" );
3200*4882a593Smuzhiyun result = QMICTLSyncProc(pDev);
3201*4882a593Smuzhiyun if (result != 0)
3202*4882a593Smuzhiyun {
3203*4882a593Smuzhiyun DBG( "QMI CTL Sync Procedure Error\n" );
3204*4882a593Smuzhiyun goto __qmi_sync_finished;
3205*4882a593Smuzhiyun }
3206*4882a593Smuzhiyun else
3207*4882a593Smuzhiyun {
3208*4882a593Smuzhiyun DBG( "QMI CTL Sync Procedure Successful\n" );
3209*4882a593Smuzhiyun }
3210*4882a593Smuzhiyun
3211*4882a593Smuzhiyun #if defined(QUECTEL_WWAN_QMAP)
3212*4882a593Smuzhiyun if (pDev->qmap_mode) {
3213*4882a593Smuzhiyun // Setup Data Format
3214*4882a593Smuzhiyun result = QMIWDASetDataFormat (pDev, pDev->qmap_mode, &pDev->qmap_size);
3215*4882a593Smuzhiyun if (result != 0)
3216*4882a593Smuzhiyun {
3217*4882a593Smuzhiyun goto __qmi_sync_finished;
3218*4882a593Smuzhiyun }
3219*4882a593Smuzhiyun pDev->mpNetDev->rx_urb_size = pDev->qmap_size;
3220*4882a593Smuzhiyun }
3221*4882a593Smuzhiyun #endif
3222*4882a593Smuzhiyun
3223*4882a593Smuzhiyun // Setup WDS callback
3224*4882a593Smuzhiyun result = SetupQMIWDSCallback( pDev );
3225*4882a593Smuzhiyun if (result != 0)
3226*4882a593Smuzhiyun {
3227*4882a593Smuzhiyun goto __qmi_sync_finished;
3228*4882a593Smuzhiyun }
3229*4882a593Smuzhiyun
3230*4882a593Smuzhiyun // Fill MEID for device
3231*4882a593Smuzhiyun result = QMIDMSGetMEID( pDev );
3232*4882a593Smuzhiyun if (result != 0)
3233*4882a593Smuzhiyun {
3234*4882a593Smuzhiyun goto __qmi_sync_finished;
3235*4882a593Smuzhiyun }
3236*4882a593Smuzhiyun #endif
3237*4882a593Smuzhiyun
3238*4882a593Smuzhiyun __qmi_sync_finished:
3239*4882a593Smuzhiyun pDev->mbQMIReady = true;
3240*4882a593Smuzhiyun complete_all(&pDev->mQMIReadyCompletion);
3241*4882a593Smuzhiyun pDev->mbQMISyncIng = false;
3242*4882a593Smuzhiyun if (atomic_dec_and_test(&pDev->refcount)) {
3243*4882a593Smuzhiyun kfree( pDev );
3244*4882a593Smuzhiyun }
3245*4882a593Smuzhiyun return result;
3246*4882a593Smuzhiyun }
3247*4882a593Smuzhiyun
3248*4882a593Smuzhiyun /*===========================================================================
3249*4882a593Smuzhiyun METHOD:
3250*4882a593Smuzhiyun RegisterQMIDevice (Public Method)
3251*4882a593Smuzhiyun
3252*4882a593Smuzhiyun DESCRIPTION:
3253*4882a593Smuzhiyun QMI Device initialization function
3254*4882a593Smuzhiyun
3255*4882a593Smuzhiyun PARAMETERS:
3256*4882a593Smuzhiyun pDev [ I ] - Device specific memory
3257*4882a593Smuzhiyun
3258*4882a593Smuzhiyun RETURN VALUE:
3259*4882a593Smuzhiyun int - 0 for success
3260*4882a593Smuzhiyun Negative errno for failure
3261*4882a593Smuzhiyun ===========================================================================*/
3262*4882a593Smuzhiyun int RegisterQMIDevice( sGobiUSBNet * pDev )
3263*4882a593Smuzhiyun {
3264*4882a593Smuzhiyun int result;
3265*4882a593Smuzhiyun int GobiQMIIndex = 0;
3266*4882a593Smuzhiyun dev_t devno;
3267*4882a593Smuzhiyun char * pDevName;
3268*4882a593Smuzhiyun
3269*4882a593Smuzhiyun if (pDev->mQMIDev.mbCdevIsInitialized == true)
3270*4882a593Smuzhiyun {
3271*4882a593Smuzhiyun // Should never happen, but always better to check
3272*4882a593Smuzhiyun DBG( "device already exists\n" );
3273*4882a593Smuzhiyun return -EEXIST;
3274*4882a593Smuzhiyun }
3275*4882a593Smuzhiyun
3276*4882a593Smuzhiyun pDev->mbQMIValid = true;
3277*4882a593Smuzhiyun pDev->mbDeregisterQMIDevice = false;
3278*4882a593Smuzhiyun
3279*4882a593Smuzhiyun // Set up for QMICTL
3280*4882a593Smuzhiyun // (does not send QMI message, just sets up memory)
3281*4882a593Smuzhiyun result = GetClientID( pDev, QMICTL );
3282*4882a593Smuzhiyun if (result != 0)
3283*4882a593Smuzhiyun {
3284*4882a593Smuzhiyun pDev->mbQMIValid = false;
3285*4882a593Smuzhiyun return result;
3286*4882a593Smuzhiyun }
3287*4882a593Smuzhiyun atomic_set( &pDev->mQMIDev.mQMICTLTransactionID, 1 );
3288*4882a593Smuzhiyun
3289*4882a593Smuzhiyun // Start Async reading
3290*4882a593Smuzhiyun result = StartRead( pDev );
3291*4882a593Smuzhiyun if (result != 0)
3292*4882a593Smuzhiyun {
3293*4882a593Smuzhiyun pDev->mbQMIValid = false;
3294*4882a593Smuzhiyun return result;
3295*4882a593Smuzhiyun }
3296*4882a593Smuzhiyun
3297*4882a593Smuzhiyun if (pDev->mbMdm9x07)
3298*4882a593Smuzhiyun {
3299*4882a593Smuzhiyun usb_control_msg( pDev->mpNetDev->udev,
3300*4882a593Smuzhiyun usb_sndctrlpipe( pDev->mpNetDev->udev, 0 ),
3301*4882a593Smuzhiyun SET_CONTROL_LINE_STATE_REQUEST,
3302*4882a593Smuzhiyun SET_CONTROL_LINE_STATE_REQUEST_TYPE,
3303*4882a593Smuzhiyun CONTROL_DTR,
3304*4882a593Smuzhiyun /* USB interface number to receive control message */
3305*4882a593Smuzhiyun pDev->mpIntf->cur_altsetting->desc.bInterfaceNumber,
3306*4882a593Smuzhiyun NULL,
3307*4882a593Smuzhiyun 0,
3308*4882a593Smuzhiyun 100 );
3309*4882a593Smuzhiyun }
3310*4882a593Smuzhiyun
3311*4882a593Smuzhiyun //for EC21&25, must wait about 15 seconds to wait QMI ready. it is too long for driver probe(will block other drivers probe).
3312*4882a593Smuzhiyun if (pDev->mbMdm9x07)
3313*4882a593Smuzhiyun {
3314*4882a593Smuzhiyun struct task_struct *qmi_sync_task;
3315*4882a593Smuzhiyun atomic_inc(&pDev->refcount);
3316*4882a593Smuzhiyun init_completion(&pDev->mQMIReadyCompletion);
3317*4882a593Smuzhiyun pDev->mbQMIReady = false;
3318*4882a593Smuzhiyun pDev->mbQMISyncIng = true;
3319*4882a593Smuzhiyun qmi_sync_task = kthread_run(qmi_sync_thread, (void *)pDev, "qmi_sync/%d", pDev->mpNetDev->udev->devnum);
3320*4882a593Smuzhiyun if (IS_ERR(qmi_sync_task)) {
3321*4882a593Smuzhiyun pDev->mbQMISyncIng = false;
3322*4882a593Smuzhiyun atomic_dec(&pDev->refcount);
3323*4882a593Smuzhiyun DBG( "Create qmi_sync_thread fail\n" );
3324*4882a593Smuzhiyun return PTR_ERR(qmi_sync_task);
3325*4882a593Smuzhiyun }
3326*4882a593Smuzhiyun goto __register_chardev_qccmi;
3327*4882a593Smuzhiyun }
3328*4882a593Smuzhiyun
3329*4882a593Smuzhiyun // Device is not ready for QMI connections right away
3330*4882a593Smuzhiyun // Wait up to 30 seconds before failing
3331*4882a593Smuzhiyun if (QMIReady( pDev, 30000 ) == false)
3332*4882a593Smuzhiyun {
3333*4882a593Smuzhiyun DBG( "Device unresponsive to QMI\n" );
3334*4882a593Smuzhiyun return -ETIMEDOUT;
3335*4882a593Smuzhiyun }
3336*4882a593Smuzhiyun
3337*4882a593Smuzhiyun // Initiate QMI CTL Sync Procedure
3338*4882a593Smuzhiyun DBG( "Sending QMI CTL Sync Request\n" );
3339*4882a593Smuzhiyun result = QMICTLSyncProc(pDev);
3340*4882a593Smuzhiyun if (result != 0)
3341*4882a593Smuzhiyun {
3342*4882a593Smuzhiyun DBG( "QMI CTL Sync Procedure Error\n" );
3343*4882a593Smuzhiyun return result;
3344*4882a593Smuzhiyun }
3345*4882a593Smuzhiyun else
3346*4882a593Smuzhiyun {
3347*4882a593Smuzhiyun DBG( "QMI CTL Sync Procedure Successful\n" );
3348*4882a593Smuzhiyun }
3349*4882a593Smuzhiyun
3350*4882a593Smuzhiyun // Setup Data Format
3351*4882a593Smuzhiyun #if defined(QUECTEL_WWAN_QMAP)
3352*4882a593Smuzhiyun result = QMIWDASetDataFormat (pDev, pDev->qmap_mode, NULL);
3353*4882a593Smuzhiyun #else
3354*4882a593Smuzhiyun result = QMIWDASetDataFormat (pDev, 0, NULL);
3355*4882a593Smuzhiyun #endif
3356*4882a593Smuzhiyun if (result != 0)
3357*4882a593Smuzhiyun {
3358*4882a593Smuzhiyun return result;
3359*4882a593Smuzhiyun }
3360*4882a593Smuzhiyun
3361*4882a593Smuzhiyun // Setup WDS callback
3362*4882a593Smuzhiyun result = SetupQMIWDSCallback( pDev );
3363*4882a593Smuzhiyun if (result != 0)
3364*4882a593Smuzhiyun {
3365*4882a593Smuzhiyun return result;
3366*4882a593Smuzhiyun }
3367*4882a593Smuzhiyun
3368*4882a593Smuzhiyun // Fill MEID for device
3369*4882a593Smuzhiyun result = QMIDMSGetMEID( pDev );
3370*4882a593Smuzhiyun if (result != 0)
3371*4882a593Smuzhiyun {
3372*4882a593Smuzhiyun return result;
3373*4882a593Smuzhiyun }
3374*4882a593Smuzhiyun
3375*4882a593Smuzhiyun __register_chardev_qccmi:
3376*4882a593Smuzhiyun // allocate and fill devno with numbers
3377*4882a593Smuzhiyun result = alloc_chrdev_region( &devno, 0, 1, "qcqmi" );
3378*4882a593Smuzhiyun if (result < 0)
3379*4882a593Smuzhiyun {
3380*4882a593Smuzhiyun return result;
3381*4882a593Smuzhiyun }
3382*4882a593Smuzhiyun
3383*4882a593Smuzhiyun // Create cdev
3384*4882a593Smuzhiyun cdev_init( &pDev->mQMIDev.mCdev, &UserspaceQMIFops );
3385*4882a593Smuzhiyun pDev->mQMIDev.mCdev.owner = THIS_MODULE;
3386*4882a593Smuzhiyun pDev->mQMIDev.mCdev.ops = &UserspaceQMIFops;
3387*4882a593Smuzhiyun pDev->mQMIDev.mbCdevIsInitialized = true;
3388*4882a593Smuzhiyun
3389*4882a593Smuzhiyun result = cdev_add( &pDev->mQMIDev.mCdev, devno, 1 );
3390*4882a593Smuzhiyun if (result != 0)
3391*4882a593Smuzhiyun {
3392*4882a593Smuzhiyun DBG( "error adding cdev\n" );
3393*4882a593Smuzhiyun return result;
3394*4882a593Smuzhiyun }
3395*4882a593Smuzhiyun
3396*4882a593Smuzhiyun // Match interface number (usb# or eth#)
3397*4882a593Smuzhiyun if (!!(pDevName = strstr( pDev->mpNetDev->net->name, "eth" ))) {
3398*4882a593Smuzhiyun pDevName += strlen( "eth" );
3399*4882a593Smuzhiyun } else if (!!(pDevName = strstr( pDev->mpNetDev->net->name, "usb" ))) {
3400*4882a593Smuzhiyun pDevName += strlen( "usb" );
3401*4882a593Smuzhiyun #if 1 //openWRT like use ppp# or lte#
3402*4882a593Smuzhiyun } else if (!!(pDevName = strstr( pDev->mpNetDev->net->name, "ppp" ))) {
3403*4882a593Smuzhiyun pDevName += strlen( "ppp" );
3404*4882a593Smuzhiyun } else if (!!(pDevName = strstr( pDev->mpNetDev->net->name, "lte" ))) {
3405*4882a593Smuzhiyun pDevName += strlen( "lte" );
3406*4882a593Smuzhiyun #endif
3407*4882a593Smuzhiyun } else {
3408*4882a593Smuzhiyun DBG( "Bad net name: %s\n", pDev->mpNetDev->net->name );
3409*4882a593Smuzhiyun return -ENXIO;
3410*4882a593Smuzhiyun }
3411*4882a593Smuzhiyun GobiQMIIndex = simple_strtoul( pDevName, NULL, 10 );
3412*4882a593Smuzhiyun if (GobiQMIIndex < 0)
3413*4882a593Smuzhiyun {
3414*4882a593Smuzhiyun DBG( "Bad minor number\n" );
3415*4882a593Smuzhiyun return -ENXIO;
3416*4882a593Smuzhiyun }
3417*4882a593Smuzhiyun
3418*4882a593Smuzhiyun // Always print this output
3419*4882a593Smuzhiyun printk( KERN_INFO "creating qcqmi%d\n",
3420*4882a593Smuzhiyun GobiQMIIndex );
3421*4882a593Smuzhiyun
3422*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION( 2,6,27 ))
3423*4882a593Smuzhiyun // kernel 2.6.27 added a new fourth parameter to device_create
3424*4882a593Smuzhiyun // void * drvdata : the data to be added to the device for callbacks
3425*4882a593Smuzhiyun device_create( pDev->mQMIDev.mpDevClass,
3426*4882a593Smuzhiyun &pDev->mpIntf->dev,
3427*4882a593Smuzhiyun devno,
3428*4882a593Smuzhiyun NULL,
3429*4882a593Smuzhiyun "qcqmi%d",
3430*4882a593Smuzhiyun GobiQMIIndex );
3431*4882a593Smuzhiyun #else
3432*4882a593Smuzhiyun device_create( pDev->mQMIDev.mpDevClass,
3433*4882a593Smuzhiyun &pDev->mpIntf->dev,
3434*4882a593Smuzhiyun devno,
3435*4882a593Smuzhiyun "qcqmi%d",
3436*4882a593Smuzhiyun GobiQMIIndex );
3437*4882a593Smuzhiyun #endif
3438*4882a593Smuzhiyun
3439*4882a593Smuzhiyun pDev->mQMIDev.mDevNum = devno;
3440*4882a593Smuzhiyun
3441*4882a593Smuzhiyun // Success
3442*4882a593Smuzhiyun return 0;
3443*4882a593Smuzhiyun }
3444*4882a593Smuzhiyun
3445*4882a593Smuzhiyun /*===========================================================================
3446*4882a593Smuzhiyun METHOD:
3447*4882a593Smuzhiyun DeregisterQMIDevice (Public Method)
3448*4882a593Smuzhiyun
3449*4882a593Smuzhiyun DESCRIPTION:
3450*4882a593Smuzhiyun QMI Device cleanup function
3451*4882a593Smuzhiyun
3452*4882a593Smuzhiyun NOTE: When this function is run the device is no longer valid
3453*4882a593Smuzhiyun
3454*4882a593Smuzhiyun PARAMETERS:
3455*4882a593Smuzhiyun pDev [ I ] - Device specific memory
3456*4882a593Smuzhiyun
3457*4882a593Smuzhiyun RETURN VALUE:
3458*4882a593Smuzhiyun None
3459*4882a593Smuzhiyun ===========================================================================*/
3460*4882a593Smuzhiyun void DeregisterQMIDevice( sGobiUSBNet * pDev )
3461*4882a593Smuzhiyun {
3462*4882a593Smuzhiyun #ifndef quectel_no_for_each_process
3463*4882a593Smuzhiyun struct inode * pOpenInode;
3464*4882a593Smuzhiyun struct list_head * pInodeList;
3465*4882a593Smuzhiyun struct task_struct * pEachTask;
3466*4882a593Smuzhiyun struct fdtable * pFDT;
3467*4882a593Smuzhiyun struct file * pFilp;
3468*4882a593Smuzhiyun int count = 0;
3469*4882a593Smuzhiyun #endif
3470*4882a593Smuzhiyun unsigned long flags;
3471*4882a593Smuzhiyun int tries;
3472*4882a593Smuzhiyun int result;
3473*4882a593Smuzhiyun
3474*4882a593Smuzhiyun // Should never happen, but check anyway
3475*4882a593Smuzhiyun if (IsDeviceValid( pDev ) == false)
3476*4882a593Smuzhiyun {
3477*4882a593Smuzhiyun DBG( "wrong device\n" );
3478*4882a593Smuzhiyun return;
3479*4882a593Smuzhiyun }
3480*4882a593Smuzhiyun
3481*4882a593Smuzhiyun pDev->mbDeregisterQMIDevice = true;
3482*4882a593Smuzhiyun
3483*4882a593Smuzhiyun for (tries = 0; tries < 3000; tries += 10) {
3484*4882a593Smuzhiyun if (pDev->mbQMISyncIng == false)
3485*4882a593Smuzhiyun break;
3486*4882a593Smuzhiyun msleep(10);
3487*4882a593Smuzhiyun }
3488*4882a593Smuzhiyun
3489*4882a593Smuzhiyun if (pDev->mbQMISyncIng) {
3490*4882a593Smuzhiyun DBG( "QMI sync ing\n" );
3491*4882a593Smuzhiyun }
3492*4882a593Smuzhiyun
3493*4882a593Smuzhiyun // Release all clients
3494*4882a593Smuzhiyun spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
3495*4882a593Smuzhiyun while (pDev->mQMIDev.mpClientMemList != NULL)
3496*4882a593Smuzhiyun {
3497*4882a593Smuzhiyun u16 mClientID = pDev->mQMIDev.mpClientMemList->mClientID;
3498*4882a593Smuzhiyun if (waitqueue_active(&pDev->mQMIDev.mpClientMemList->mWaitQueue)) {
3499*4882a593Smuzhiyun DBG("WaitQueue 0x%04X\n", mClientID);
3500*4882a593Smuzhiyun wake_up_interruptible_sync( &pDev->mQMIDev.mpClientMemList->mWaitQueue );
3501*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
3502*4882a593Smuzhiyun msleep(10);
3503*4882a593Smuzhiyun spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
3504*4882a593Smuzhiyun continue;
3505*4882a593Smuzhiyun }
3506*4882a593Smuzhiyun
3507*4882a593Smuzhiyun DBG( "release 0x%04X\n", pDev->mQMIDev.mpClientMemList->mClientID );
3508*4882a593Smuzhiyun
3509*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
3510*4882a593Smuzhiyun ReleaseClientID( pDev, mClientID );
3511*4882a593Smuzhiyun // NOTE: pDev->mQMIDev.mpClientMemList will
3512*4882a593Smuzhiyun // be updated in ReleaseClientID()
3513*4882a593Smuzhiyun spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
3514*4882a593Smuzhiyun }
3515*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
3516*4882a593Smuzhiyun
3517*4882a593Smuzhiyun // Stop all reads
3518*4882a593Smuzhiyun KillRead( pDev );
3519*4882a593Smuzhiyun
3520*4882a593Smuzhiyun pDev->mbQMIValid = false;
3521*4882a593Smuzhiyun
3522*4882a593Smuzhiyun if (pDev->mQMIDev.mbCdevIsInitialized == false)
3523*4882a593Smuzhiyun {
3524*4882a593Smuzhiyun return;
3525*4882a593Smuzhiyun }
3526*4882a593Smuzhiyun
3527*4882a593Smuzhiyun #ifndef quectel_no_for_each_process
3528*4882a593Smuzhiyun // Find each open file handle, and manually close it
3529*4882a593Smuzhiyun
3530*4882a593Smuzhiyun // Generally there will only be only one inode, but more are possible
3531*4882a593Smuzhiyun list_for_each( pInodeList, &pDev->mQMIDev.mCdev.list )
3532*4882a593Smuzhiyun {
3533*4882a593Smuzhiyun // Get the inode
3534*4882a593Smuzhiyun pOpenInode = container_of( pInodeList, struct inode, i_devices );
3535*4882a593Smuzhiyun if (pOpenInode != NULL && (IS_ERR( pOpenInode ) == false))
3536*4882a593Smuzhiyun {
3537*4882a593Smuzhiyun // Look for this inode in each task
3538*4882a593Smuzhiyun
3539*4882a593Smuzhiyun rcu_read_lock();
3540*4882a593Smuzhiyun for_each_process( pEachTask )
3541*4882a593Smuzhiyun {
3542*4882a593Smuzhiyun task_lock(pEachTask);
3543*4882a593Smuzhiyun if (pEachTask == NULL || pEachTask->files == NULL)
3544*4882a593Smuzhiyun {
3545*4882a593Smuzhiyun // Some tasks may not have files (e.g. Xsession)
3546*4882a593Smuzhiyun task_unlock(pEachTask);
3547*4882a593Smuzhiyun continue;
3548*4882a593Smuzhiyun }
3549*4882a593Smuzhiyun // For each file this task has open, check if it's referencing
3550*4882a593Smuzhiyun // our inode.
3551*4882a593Smuzhiyun spin_lock_irqsave( &pEachTask->files->file_lock, flags );
3552*4882a593Smuzhiyun task_unlock(pEachTask); //kernel/exit.c:do_exit() -> fs/file.c:exit_files()
3553*4882a593Smuzhiyun pFDT = files_fdtable( pEachTask->files );
3554*4882a593Smuzhiyun for (count = 0; count < pFDT->max_fds; count++)
3555*4882a593Smuzhiyun {
3556*4882a593Smuzhiyun pFilp = pFDT->fd[count];
3557*4882a593Smuzhiyun if (pFilp != NULL && pFilp->f_dentry != NULL)
3558*4882a593Smuzhiyun {
3559*4882a593Smuzhiyun if (pFilp->f_dentry->d_inode == pOpenInode)
3560*4882a593Smuzhiyun {
3561*4882a593Smuzhiyun // Close this file handle
3562*4882a593Smuzhiyun rcu_assign_pointer( pFDT->fd[count], NULL );
3563*4882a593Smuzhiyun spin_unlock_irqrestore( &pEachTask->files->file_lock, flags );
3564*4882a593Smuzhiyun
3565*4882a593Smuzhiyun DBG( "forcing close of open file handle\n" );
3566*4882a593Smuzhiyun filp_close( pFilp, pEachTask->files );
3567*4882a593Smuzhiyun
3568*4882a593Smuzhiyun spin_lock_irqsave( &pEachTask->files->file_lock, flags );
3569*4882a593Smuzhiyun }
3570*4882a593Smuzhiyun }
3571*4882a593Smuzhiyun }
3572*4882a593Smuzhiyun spin_unlock_irqrestore( &pEachTask->files->file_lock, flags );
3573*4882a593Smuzhiyun }
3574*4882a593Smuzhiyun rcu_read_unlock();
3575*4882a593Smuzhiyun }
3576*4882a593Smuzhiyun }
3577*4882a593Smuzhiyun #endif
3578*4882a593Smuzhiyun
3579*4882a593Smuzhiyun if (pDev->mpNetDev->udev->state) {
3580*4882a593Smuzhiyun // Send SetControlLineState request (USB_CDC)
3581*4882a593Smuzhiyun result = usb_control_msg( pDev->mpNetDev->udev,
3582*4882a593Smuzhiyun usb_sndctrlpipe( pDev->mpNetDev->udev, 0 ),
3583*4882a593Smuzhiyun SET_CONTROL_LINE_STATE_REQUEST,
3584*4882a593Smuzhiyun SET_CONTROL_LINE_STATE_REQUEST_TYPE,
3585*4882a593Smuzhiyun 0, // DTR not present
3586*4882a593Smuzhiyun /* USB interface number to receive control message */
3587*4882a593Smuzhiyun pDev->mpIntf->cur_altsetting->desc.bInterfaceNumber,
3588*4882a593Smuzhiyun NULL,
3589*4882a593Smuzhiyun 0,
3590*4882a593Smuzhiyun 100 );
3591*4882a593Smuzhiyun if (result < 0)
3592*4882a593Smuzhiyun {
3593*4882a593Smuzhiyun DBG( "Bad SetControlLineState status %d\n", result );
3594*4882a593Smuzhiyun }
3595*4882a593Smuzhiyun }
3596*4882a593Smuzhiyun
3597*4882a593Smuzhiyun // Remove device (so no more calls can be made by users)
3598*4882a593Smuzhiyun if (IS_ERR( pDev->mQMIDev.mpDevClass ) == false)
3599*4882a593Smuzhiyun {
3600*4882a593Smuzhiyun device_destroy( pDev->mQMIDev.mpDevClass,
3601*4882a593Smuzhiyun pDev->mQMIDev.mDevNum );
3602*4882a593Smuzhiyun }
3603*4882a593Smuzhiyun
3604*4882a593Smuzhiyun // Hold onto cdev memory location until everyone is through using it.
3605*4882a593Smuzhiyun // Timeout after 30 seconds (10 ms interval). Timeout should never happen,
3606*4882a593Smuzhiyun // but exists to prevent an infinate loop just in case.
3607*4882a593Smuzhiyun for (tries = 0; tries < 30 * 100; tries++)
3608*4882a593Smuzhiyun {
3609*4882a593Smuzhiyun #if (LINUX_VERSION_CODE < KERNEL_VERSION( 4,11,0 ))
3610*4882a593Smuzhiyun int ref = atomic_read( &pDev->mQMIDev.mCdev.kobj.kref.refcount );
3611*4882a593Smuzhiyun #else
3612*4882a593Smuzhiyun int ref = kref_read( &pDev->mQMIDev.mCdev.kobj.kref );
3613*4882a593Smuzhiyun #endif
3614*4882a593Smuzhiyun if (ref > 1)
3615*4882a593Smuzhiyun {
3616*4882a593Smuzhiyun DBG( "cdev in use by %d tasks\n", ref - 1 );
3617*4882a593Smuzhiyun if (tries > 10)
3618*4882a593Smuzhiyun INFO( "cdev in use by %d tasks\n", ref - 1 );
3619*4882a593Smuzhiyun msleep( 10 );
3620*4882a593Smuzhiyun }
3621*4882a593Smuzhiyun else
3622*4882a593Smuzhiyun {
3623*4882a593Smuzhiyun break;
3624*4882a593Smuzhiyun }
3625*4882a593Smuzhiyun }
3626*4882a593Smuzhiyun
3627*4882a593Smuzhiyun cdev_del( &pDev->mQMIDev.mCdev );
3628*4882a593Smuzhiyun
3629*4882a593Smuzhiyun unregister_chrdev_region( pDev->mQMIDev.mDevNum, 1 );
3630*4882a593Smuzhiyun
3631*4882a593Smuzhiyun return;
3632*4882a593Smuzhiyun }
3633*4882a593Smuzhiyun
3634*4882a593Smuzhiyun /*=========================================================================*/
3635*4882a593Smuzhiyun // Driver level client management
3636*4882a593Smuzhiyun /*=========================================================================*/
3637*4882a593Smuzhiyun
3638*4882a593Smuzhiyun /*===========================================================================
3639*4882a593Smuzhiyun METHOD:
3640*4882a593Smuzhiyun QMIReady (Public Method)
3641*4882a593Smuzhiyun
3642*4882a593Smuzhiyun DESCRIPTION:
3643*4882a593Smuzhiyun Send QMI CTL GET VERSION INFO REQ and SET DATA FORMAT REQ
3644*4882a593Smuzhiyun Wait for response or timeout
3645*4882a593Smuzhiyun
3646*4882a593Smuzhiyun PARAMETERS:
3647*4882a593Smuzhiyun pDev [ I ] - Device specific memory
3648*4882a593Smuzhiyun timeout [ I ] - Milliseconds to wait for response
3649*4882a593Smuzhiyun
3650*4882a593Smuzhiyun RETURN VALUE:
3651*4882a593Smuzhiyun bool
3652*4882a593Smuzhiyun ===========================================================================*/
3653*4882a593Smuzhiyun static bool QMIReady(
3654*4882a593Smuzhiyun sGobiUSBNet * pDev,
3655*4882a593Smuzhiyun u16 timeout )
3656*4882a593Smuzhiyun {
3657*4882a593Smuzhiyun int result;
3658*4882a593Smuzhiyun void * pWriteBuffer;
3659*4882a593Smuzhiyun u16 writeBufferSize;
3660*4882a593Smuzhiyun void * pReadBuffer;
3661*4882a593Smuzhiyun u16 readBufferSize;
3662*4882a593Smuzhiyun u16 curTime;
3663*4882a593Smuzhiyun unsigned long flags;
3664*4882a593Smuzhiyun u8 transactionID;
3665*4882a593Smuzhiyun u16 interval = 2000;
3666*4882a593Smuzhiyun
3667*4882a593Smuzhiyun if (IsDeviceValid( pDev ) == false)
3668*4882a593Smuzhiyun {
3669*4882a593Smuzhiyun DBG( "Invalid device\n" );
3670*4882a593Smuzhiyun return false;
3671*4882a593Smuzhiyun }
3672*4882a593Smuzhiyun
3673*4882a593Smuzhiyun writeBufferSize = QMICTLReadyReqSize();
3674*4882a593Smuzhiyun pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL );
3675*4882a593Smuzhiyun if (pWriteBuffer == NULL)
3676*4882a593Smuzhiyun {
3677*4882a593Smuzhiyun return false;
3678*4882a593Smuzhiyun }
3679*4882a593Smuzhiyun
3680*4882a593Smuzhiyun // An implimentation of down_timeout has not been agreed on,
3681*4882a593Smuzhiyun // so it's been added and removed from the kernel several times.
3682*4882a593Smuzhiyun // We're just going to ignore it and poll the semaphore.
3683*4882a593Smuzhiyun
3684*4882a593Smuzhiyun // Send a write every 1000 ms and see if we get a response
3685*4882a593Smuzhiyun for (curTime = 0; curTime < timeout; curTime += interval)
3686*4882a593Smuzhiyun {
3687*4882a593Smuzhiyun // Start read
3688*4882a593Smuzhiyun struct QuecSem *readSem = kmalloc(sizeof(struct QuecSem ), GFP_KERNEL);
3689*4882a593Smuzhiyun readSem->magic = QUEC_SEM_MAGIC;
3690*4882a593Smuzhiyun sema_init( &readSem->readSem, 0 );
3691*4882a593Smuzhiyun
3692*4882a593Smuzhiyun transactionID = QMIXactionIDGet( pDev );
3693*4882a593Smuzhiyun
3694*4882a593Smuzhiyun result = ReadAsync( pDev, QMICTL, transactionID, UpSem, readSem );
3695*4882a593Smuzhiyun if (result != 0)
3696*4882a593Smuzhiyun {
3697*4882a593Smuzhiyun kfree( pWriteBuffer );
3698*4882a593Smuzhiyun return false;
3699*4882a593Smuzhiyun }
3700*4882a593Smuzhiyun
3701*4882a593Smuzhiyun // Fill buffer
3702*4882a593Smuzhiyun result = QMICTLReadyReq( pWriteBuffer,
3703*4882a593Smuzhiyun writeBufferSize,
3704*4882a593Smuzhiyun transactionID );
3705*4882a593Smuzhiyun if (result < 0)
3706*4882a593Smuzhiyun {
3707*4882a593Smuzhiyun kfree( pWriteBuffer );
3708*4882a593Smuzhiyun return false;
3709*4882a593Smuzhiyun }
3710*4882a593Smuzhiyun
3711*4882a593Smuzhiyun // Disregard status. On errors, just try again
3712*4882a593Smuzhiyun result = WriteSync( pDev,
3713*4882a593Smuzhiyun pWriteBuffer,
3714*4882a593Smuzhiyun writeBufferSize,
3715*4882a593Smuzhiyun QMICTL );
3716*4882a593Smuzhiyun
3717*4882a593Smuzhiyun if (result < 0) //maybe caused by usb disconnect
3718*4882a593Smuzhiyun {
3719*4882a593Smuzhiyun kfree( pWriteBuffer );
3720*4882a593Smuzhiyun return false;
3721*4882a593Smuzhiyun }
3722*4882a593Smuzhiyun
3723*4882a593Smuzhiyun #if 1
3724*4882a593Smuzhiyun if (down_timeout( &readSem->readSem, msecs_to_jiffies(interval) ) == 0)
3725*4882a593Smuzhiyun #else
3726*4882a593Smuzhiyun msleep( interval );
3727*4882a593Smuzhiyun if (down_trylock( &readSem->readSem ) == 0)
3728*4882a593Smuzhiyun #endif
3729*4882a593Smuzhiyun {
3730*4882a593Smuzhiyun kfree(readSem);
3731*4882a593Smuzhiyun // Enter critical section
3732*4882a593Smuzhiyun spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
3733*4882a593Smuzhiyun
3734*4882a593Smuzhiyun // Pop the read data
3735*4882a593Smuzhiyun if (PopFromReadMemList( pDev,
3736*4882a593Smuzhiyun QMICTL,
3737*4882a593Smuzhiyun transactionID,
3738*4882a593Smuzhiyun &pReadBuffer,
3739*4882a593Smuzhiyun &readBufferSize ) == true)
3740*4882a593Smuzhiyun {
3741*4882a593Smuzhiyun // Success
3742*4882a593Smuzhiyun
3743*4882a593Smuzhiyun // End critical section
3744*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
3745*4882a593Smuzhiyun
3746*4882a593Smuzhiyun // We don't care about the result
3747*4882a593Smuzhiyun kfree( pReadBuffer );
3748*4882a593Smuzhiyun
3749*4882a593Smuzhiyun break;
3750*4882a593Smuzhiyun }
3751*4882a593Smuzhiyun else
3752*4882a593Smuzhiyun {
3753*4882a593Smuzhiyun // Read mismatch/failure, unlock and continue
3754*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
3755*4882a593Smuzhiyun }
3756*4882a593Smuzhiyun }
3757*4882a593Smuzhiyun else
3758*4882a593Smuzhiyun {
3759*4882a593Smuzhiyun readSem->magic = 0;
3760*4882a593Smuzhiyun // Enter critical section
3761*4882a593Smuzhiyun spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
3762*4882a593Smuzhiyun
3763*4882a593Smuzhiyun // Timeout, remove the async read
3764*4882a593Smuzhiyun NotifyAndPopNotifyList( pDev, QMICTL, transactionID );
3765*4882a593Smuzhiyun
3766*4882a593Smuzhiyun // End critical section
3767*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
3768*4882a593Smuzhiyun }
3769*4882a593Smuzhiyun
3770*4882a593Smuzhiyun if (pDev->mbDeregisterQMIDevice)
3771*4882a593Smuzhiyun {
3772*4882a593Smuzhiyun kfree( pWriteBuffer );
3773*4882a593Smuzhiyun return false;
3774*4882a593Smuzhiyun }
3775*4882a593Smuzhiyun }
3776*4882a593Smuzhiyun
3777*4882a593Smuzhiyun kfree( pWriteBuffer );
3778*4882a593Smuzhiyun
3779*4882a593Smuzhiyun // Did we time out?
3780*4882a593Smuzhiyun if (curTime >= timeout)
3781*4882a593Smuzhiyun {
3782*4882a593Smuzhiyun return false;
3783*4882a593Smuzhiyun }
3784*4882a593Smuzhiyun
3785*4882a593Smuzhiyun DBG( "QMI Ready after %u milliseconds\n", curTime );
3786*4882a593Smuzhiyun
3787*4882a593Smuzhiyun // Success
3788*4882a593Smuzhiyun return true;
3789*4882a593Smuzhiyun }
3790*4882a593Smuzhiyun
3791*4882a593Smuzhiyun /*===========================================================================
3792*4882a593Smuzhiyun METHOD:
3793*4882a593Smuzhiyun QMIWDSCallback (Public Method)
3794*4882a593Smuzhiyun
3795*4882a593Smuzhiyun DESCRIPTION:
3796*4882a593Smuzhiyun QMI WDS callback function
3797*4882a593Smuzhiyun Update net stats or link state
3798*4882a593Smuzhiyun
3799*4882a593Smuzhiyun PARAMETERS:
3800*4882a593Smuzhiyun pDev [ I ] - Device specific memory
3801*4882a593Smuzhiyun clientID [ I ] - Client ID
3802*4882a593Smuzhiyun pData [ I ] - Callback data (unused)
3803*4882a593Smuzhiyun
3804*4882a593Smuzhiyun RETURN VALUE:
3805*4882a593Smuzhiyun None
3806*4882a593Smuzhiyun ===========================================================================*/
3807*4882a593Smuzhiyun static void QMIWDSCallback(
3808*4882a593Smuzhiyun sGobiUSBNet * pDev,
3809*4882a593Smuzhiyun u16 clientID,
3810*4882a593Smuzhiyun void * pData )
3811*4882a593Smuzhiyun {
3812*4882a593Smuzhiyun bool bRet;
3813*4882a593Smuzhiyun int result;
3814*4882a593Smuzhiyun void * pReadBuffer;
3815*4882a593Smuzhiyun u16 readBufferSize;
3816*4882a593Smuzhiyun
3817*4882a593Smuzhiyun #if 0
3818*4882a593Smuzhiyun #if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,31 ))
3819*4882a593Smuzhiyun struct net_device_stats * pStats = &(pDev->mpNetDev->stats);
3820*4882a593Smuzhiyun #else
3821*4882a593Smuzhiyun struct net_device_stats * pStats = &(pDev->mpNetDev->net->stats);
3822*4882a593Smuzhiyun #endif
3823*4882a593Smuzhiyun #endif
3824*4882a593Smuzhiyun
3825*4882a593Smuzhiyun u32 TXOk = (u32)-1;
3826*4882a593Smuzhiyun u32 RXOk = (u32)-1;
3827*4882a593Smuzhiyun u32 TXErr = (u32)-1;
3828*4882a593Smuzhiyun u32 RXErr = (u32)-1;
3829*4882a593Smuzhiyun u32 TXOfl = (u32)-1;
3830*4882a593Smuzhiyun u32 RXOfl = (u32)-1;
3831*4882a593Smuzhiyun u64 TXBytesOk = (u64)-1;
3832*4882a593Smuzhiyun u64 RXBytesOk = (u64)-1;
3833*4882a593Smuzhiyun bool bLinkState;
3834*4882a593Smuzhiyun bool bReconfigure;
3835*4882a593Smuzhiyun unsigned long flags;
3836*4882a593Smuzhiyun
3837*4882a593Smuzhiyun if (IsDeviceValid( pDev ) == false)
3838*4882a593Smuzhiyun {
3839*4882a593Smuzhiyun DBG( "Invalid device\n" );
3840*4882a593Smuzhiyun return;
3841*4882a593Smuzhiyun }
3842*4882a593Smuzhiyun
3843*4882a593Smuzhiyun // Critical section
3844*4882a593Smuzhiyun spin_lock_irqsave( &pDev->mQMIDev.mClientMemLock, flags );
3845*4882a593Smuzhiyun
3846*4882a593Smuzhiyun bRet = PopFromReadMemList( pDev,
3847*4882a593Smuzhiyun clientID,
3848*4882a593Smuzhiyun 0,
3849*4882a593Smuzhiyun &pReadBuffer,
3850*4882a593Smuzhiyun &readBufferSize );
3851*4882a593Smuzhiyun
3852*4882a593Smuzhiyun // End critical section
3853*4882a593Smuzhiyun spin_unlock_irqrestore( &pDev->mQMIDev.mClientMemLock, flags );
3854*4882a593Smuzhiyun
3855*4882a593Smuzhiyun if (bRet == false)
3856*4882a593Smuzhiyun {
3857*4882a593Smuzhiyun DBG( "WDS callback failed to get data\n" );
3858*4882a593Smuzhiyun return;
3859*4882a593Smuzhiyun }
3860*4882a593Smuzhiyun
3861*4882a593Smuzhiyun // Default values
3862*4882a593Smuzhiyun bLinkState = ! GobiTestDownReason( pDev, NO_NDIS_CONNECTION );
3863*4882a593Smuzhiyun bReconfigure = false;
3864*4882a593Smuzhiyun
3865*4882a593Smuzhiyun result = QMIWDSEventResp( pReadBuffer,
3866*4882a593Smuzhiyun readBufferSize,
3867*4882a593Smuzhiyun &TXOk,
3868*4882a593Smuzhiyun &RXOk,
3869*4882a593Smuzhiyun &TXErr,
3870*4882a593Smuzhiyun &RXErr,
3871*4882a593Smuzhiyun &TXOfl,
3872*4882a593Smuzhiyun &RXOfl,
3873*4882a593Smuzhiyun &TXBytesOk,
3874*4882a593Smuzhiyun &RXBytesOk,
3875*4882a593Smuzhiyun &bLinkState,
3876*4882a593Smuzhiyun &bReconfigure );
3877*4882a593Smuzhiyun if (result < 0)
3878*4882a593Smuzhiyun {
3879*4882a593Smuzhiyun DBG( "bad WDS packet\n" );
3880*4882a593Smuzhiyun }
3881*4882a593Smuzhiyun else
3882*4882a593Smuzhiyun {
3883*4882a593Smuzhiyun #if 0 //usbbet.c will do this job
3884*4882a593Smuzhiyun // Fill in new values, ignore max values
3885*4882a593Smuzhiyun if (TXOfl != (u32)-1)
3886*4882a593Smuzhiyun {
3887*4882a593Smuzhiyun pStats->tx_fifo_errors = TXOfl;
3888*4882a593Smuzhiyun }
3889*4882a593Smuzhiyun
3890*4882a593Smuzhiyun if (RXOfl != (u32)-1)
3891*4882a593Smuzhiyun {
3892*4882a593Smuzhiyun pStats->rx_fifo_errors = RXOfl;
3893*4882a593Smuzhiyun }
3894*4882a593Smuzhiyun
3895*4882a593Smuzhiyun if (TXErr != (u32)-1)
3896*4882a593Smuzhiyun {
3897*4882a593Smuzhiyun pStats->tx_errors = TXErr;
3898*4882a593Smuzhiyun }
3899*4882a593Smuzhiyun
3900*4882a593Smuzhiyun if (RXErr != (u32)-1)
3901*4882a593Smuzhiyun {
3902*4882a593Smuzhiyun pStats->rx_errors = RXErr;
3903*4882a593Smuzhiyun }
3904*4882a593Smuzhiyun
3905*4882a593Smuzhiyun if (TXOk != (u32)-1)
3906*4882a593Smuzhiyun {
3907*4882a593Smuzhiyun pStats->tx_packets = TXOk + pStats->tx_errors;
3908*4882a593Smuzhiyun }
3909*4882a593Smuzhiyun
3910*4882a593Smuzhiyun if (RXOk != (u32)-1)
3911*4882a593Smuzhiyun {
3912*4882a593Smuzhiyun pStats->rx_packets = RXOk + pStats->rx_errors;
3913*4882a593Smuzhiyun }
3914*4882a593Smuzhiyun
3915*4882a593Smuzhiyun if (TXBytesOk != (u64)-1)
3916*4882a593Smuzhiyun {
3917*4882a593Smuzhiyun pStats->tx_bytes = TXBytesOk;
3918*4882a593Smuzhiyun }
3919*4882a593Smuzhiyun
3920*4882a593Smuzhiyun if (RXBytesOk != (u64)-1)
3921*4882a593Smuzhiyun {
3922*4882a593Smuzhiyun pStats->rx_bytes = RXBytesOk;
3923*4882a593Smuzhiyun }
3924*4882a593Smuzhiyun #endif
3925*4882a593Smuzhiyun
3926*4882a593Smuzhiyun if (bReconfigure == true)
3927*4882a593Smuzhiyun {
3928*4882a593Smuzhiyun DBG( "Net device link reset\n" );
3929*4882a593Smuzhiyun GobiSetDownReason( pDev, NO_NDIS_CONNECTION );
3930*4882a593Smuzhiyun GobiClearDownReason( pDev, NO_NDIS_CONNECTION );
3931*4882a593Smuzhiyun }
3932*4882a593Smuzhiyun else
3933*4882a593Smuzhiyun {
3934*4882a593Smuzhiyun if (bLinkState == true)
3935*4882a593Smuzhiyun {
3936*4882a593Smuzhiyun if (GobiTestDownReason( pDev, NO_NDIS_CONNECTION )) {
3937*4882a593Smuzhiyun DBG( "Net device link is connected\n" );
3938*4882a593Smuzhiyun GobiClearDownReason( pDev, NO_NDIS_CONNECTION );
3939*4882a593Smuzhiyun }
3940*4882a593Smuzhiyun }
3941*4882a593Smuzhiyun else
3942*4882a593Smuzhiyun {
3943*4882a593Smuzhiyun if (!GobiTestDownReason( pDev, NO_NDIS_CONNECTION )) {
3944*4882a593Smuzhiyun DBG( "Net device link is disconnected\n" );
3945*4882a593Smuzhiyun GobiSetDownReason( pDev, NO_NDIS_CONNECTION );
3946*4882a593Smuzhiyun }
3947*4882a593Smuzhiyun }
3948*4882a593Smuzhiyun }
3949*4882a593Smuzhiyun }
3950*4882a593Smuzhiyun
3951*4882a593Smuzhiyun kfree( pReadBuffer );
3952*4882a593Smuzhiyun
3953*4882a593Smuzhiyun // Setup next read
3954*4882a593Smuzhiyun result = ReadAsync( pDev,
3955*4882a593Smuzhiyun clientID,
3956*4882a593Smuzhiyun 0,
3957*4882a593Smuzhiyun QMIWDSCallback,
3958*4882a593Smuzhiyun pData );
3959*4882a593Smuzhiyun if (result != 0)
3960*4882a593Smuzhiyun {
3961*4882a593Smuzhiyun DBG( "unable to setup next async read\n" );
3962*4882a593Smuzhiyun }
3963*4882a593Smuzhiyun
3964*4882a593Smuzhiyun return;
3965*4882a593Smuzhiyun }
3966*4882a593Smuzhiyun
3967*4882a593Smuzhiyun /*===========================================================================
3968*4882a593Smuzhiyun METHOD:
3969*4882a593Smuzhiyun SetupQMIWDSCallback (Public Method)
3970*4882a593Smuzhiyun
3971*4882a593Smuzhiyun DESCRIPTION:
3972*4882a593Smuzhiyun Request client and fire off reqests and start async read for
3973*4882a593Smuzhiyun QMI WDS callback
3974*4882a593Smuzhiyun
3975*4882a593Smuzhiyun PARAMETERS:
3976*4882a593Smuzhiyun pDev [ I ] - Device specific memory
3977*4882a593Smuzhiyun
3978*4882a593Smuzhiyun RETURN VALUE:
3979*4882a593Smuzhiyun int - 0 for success
3980*4882a593Smuzhiyun Negative errno for failure
3981*4882a593Smuzhiyun ===========================================================================*/
3982*4882a593Smuzhiyun static int SetupQMIWDSCallback( sGobiUSBNet * pDev )
3983*4882a593Smuzhiyun {
3984*4882a593Smuzhiyun int result;
3985*4882a593Smuzhiyun void * pWriteBuffer;
3986*4882a593Smuzhiyun u16 writeBufferSize;
3987*4882a593Smuzhiyun u16 WDSClientID;
3988*4882a593Smuzhiyun
3989*4882a593Smuzhiyun if (IsDeviceValid( pDev ) == false)
3990*4882a593Smuzhiyun {
3991*4882a593Smuzhiyun DBG( "Invalid device\n" );
3992*4882a593Smuzhiyun return -EFAULT;
3993*4882a593Smuzhiyun }
3994*4882a593Smuzhiyun
3995*4882a593Smuzhiyun result = GetClientID( pDev, QMIWDS );
3996*4882a593Smuzhiyun if (result < 0)
3997*4882a593Smuzhiyun {
3998*4882a593Smuzhiyun return result;
3999*4882a593Smuzhiyun }
4000*4882a593Smuzhiyun WDSClientID = result;
4001*4882a593Smuzhiyun
4002*4882a593Smuzhiyun #if 0 // add for "AT$QCRMCALL=1,1", be careful: donot enable these codes if use quectel-CM, or cannot obtain IP by udhcpc
4003*4882a593Smuzhiyun if (pDev->mbMdm9x07)
4004*4882a593Smuzhiyun {
4005*4882a593Smuzhiyun void * pReadBuffer;
4006*4882a593Smuzhiyun u16 readBufferSize;
4007*4882a593Smuzhiyun
4008*4882a593Smuzhiyun writeBufferSize = QMIWDSSetQMUXBindMuxDataPortSize();
4009*4882a593Smuzhiyun pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL );
4010*4882a593Smuzhiyun if (pWriteBuffer == NULL)
4011*4882a593Smuzhiyun {
4012*4882a593Smuzhiyun return -ENOMEM;
4013*4882a593Smuzhiyun }
4014*4882a593Smuzhiyun
4015*4882a593Smuzhiyun result = QMIWDSSetQMUXBindMuxDataPortReq( pWriteBuffer,
4016*4882a593Smuzhiyun writeBufferSize,
4017*4882a593Smuzhiyun 0x81,
4018*4882a593Smuzhiyun 3 );
4019*4882a593Smuzhiyun if (result < 0)
4020*4882a593Smuzhiyun {
4021*4882a593Smuzhiyun kfree( pWriteBuffer );
4022*4882a593Smuzhiyun return result;
4023*4882a593Smuzhiyun }
4024*4882a593Smuzhiyun
4025*4882a593Smuzhiyun result = WriteSync( pDev,
4026*4882a593Smuzhiyun pWriteBuffer,
4027*4882a593Smuzhiyun writeBufferSize,
4028*4882a593Smuzhiyun WDSClientID );
4029*4882a593Smuzhiyun kfree( pWriteBuffer );
4030*4882a593Smuzhiyun
4031*4882a593Smuzhiyun if (result < 0)
4032*4882a593Smuzhiyun {
4033*4882a593Smuzhiyun return result;
4034*4882a593Smuzhiyun }
4035*4882a593Smuzhiyun
4036*4882a593Smuzhiyun result = ReadSync( pDev,
4037*4882a593Smuzhiyun &pReadBuffer,
4038*4882a593Smuzhiyun WDSClientID,
4039*4882a593Smuzhiyun 3 );
4040*4882a593Smuzhiyun if (result < 0)
4041*4882a593Smuzhiyun {
4042*4882a593Smuzhiyun return result;
4043*4882a593Smuzhiyun }
4044*4882a593Smuzhiyun readBufferSize = result;
4045*4882a593Smuzhiyun
4046*4882a593Smuzhiyun kfree( pReadBuffer );
4047*4882a593Smuzhiyun }
4048*4882a593Smuzhiyun #endif
4049*4882a593Smuzhiyun
4050*4882a593Smuzhiyun // QMI WDS Set Event Report
4051*4882a593Smuzhiyun writeBufferSize = QMIWDSSetEventReportReqSize();
4052*4882a593Smuzhiyun pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL );
4053*4882a593Smuzhiyun if (pWriteBuffer == NULL)
4054*4882a593Smuzhiyun {
4055*4882a593Smuzhiyun return -ENOMEM;
4056*4882a593Smuzhiyun }
4057*4882a593Smuzhiyun
4058*4882a593Smuzhiyun result = QMIWDSSetEventReportReq( pWriteBuffer,
4059*4882a593Smuzhiyun writeBufferSize,
4060*4882a593Smuzhiyun 1 );
4061*4882a593Smuzhiyun if (result < 0)
4062*4882a593Smuzhiyun {
4063*4882a593Smuzhiyun kfree( pWriteBuffer );
4064*4882a593Smuzhiyun return result;
4065*4882a593Smuzhiyun }
4066*4882a593Smuzhiyun
4067*4882a593Smuzhiyun result = WriteSync( pDev,
4068*4882a593Smuzhiyun pWriteBuffer,
4069*4882a593Smuzhiyun writeBufferSize,
4070*4882a593Smuzhiyun WDSClientID );
4071*4882a593Smuzhiyun kfree( pWriteBuffer );
4072*4882a593Smuzhiyun
4073*4882a593Smuzhiyun if (result < 0)
4074*4882a593Smuzhiyun {
4075*4882a593Smuzhiyun return result;
4076*4882a593Smuzhiyun }
4077*4882a593Smuzhiyun
4078*4882a593Smuzhiyun // QMI WDS Get PKG SRVC Status
4079*4882a593Smuzhiyun writeBufferSize = QMIWDSGetPKGSRVCStatusReqSize();
4080*4882a593Smuzhiyun pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL );
4081*4882a593Smuzhiyun if (pWriteBuffer == NULL)
4082*4882a593Smuzhiyun {
4083*4882a593Smuzhiyun return -ENOMEM;
4084*4882a593Smuzhiyun }
4085*4882a593Smuzhiyun
4086*4882a593Smuzhiyun result = QMIWDSGetPKGSRVCStatusReq( pWriteBuffer,
4087*4882a593Smuzhiyun writeBufferSize,
4088*4882a593Smuzhiyun 2 );
4089*4882a593Smuzhiyun if (result < 0)
4090*4882a593Smuzhiyun {
4091*4882a593Smuzhiyun kfree( pWriteBuffer );
4092*4882a593Smuzhiyun return result;
4093*4882a593Smuzhiyun }
4094*4882a593Smuzhiyun
4095*4882a593Smuzhiyun result = WriteSync( pDev,
4096*4882a593Smuzhiyun pWriteBuffer,
4097*4882a593Smuzhiyun writeBufferSize,
4098*4882a593Smuzhiyun WDSClientID );
4099*4882a593Smuzhiyun kfree( pWriteBuffer );
4100*4882a593Smuzhiyun
4101*4882a593Smuzhiyun if (result < 0)
4102*4882a593Smuzhiyun {
4103*4882a593Smuzhiyun return result;
4104*4882a593Smuzhiyun }
4105*4882a593Smuzhiyun
4106*4882a593Smuzhiyun // Setup asnyc read callback
4107*4882a593Smuzhiyun result = ReadAsync( pDev,
4108*4882a593Smuzhiyun WDSClientID,
4109*4882a593Smuzhiyun 0,
4110*4882a593Smuzhiyun QMIWDSCallback,
4111*4882a593Smuzhiyun NULL );
4112*4882a593Smuzhiyun if (result != 0)
4113*4882a593Smuzhiyun {
4114*4882a593Smuzhiyun DBG( "unable to setup async read\n" );
4115*4882a593Smuzhiyun return result;
4116*4882a593Smuzhiyun }
4117*4882a593Smuzhiyun
4118*4882a593Smuzhiyun // Send SetControlLineState request (USB_CDC)
4119*4882a593Smuzhiyun // Required for Autoconnect
4120*4882a593Smuzhiyun result = usb_control_msg( pDev->mpNetDev->udev,
4121*4882a593Smuzhiyun usb_sndctrlpipe( pDev->mpNetDev->udev, 0 ),
4122*4882a593Smuzhiyun SET_CONTROL_LINE_STATE_REQUEST,
4123*4882a593Smuzhiyun SET_CONTROL_LINE_STATE_REQUEST_TYPE,
4124*4882a593Smuzhiyun CONTROL_DTR,
4125*4882a593Smuzhiyun /* USB interface number to receive control message */
4126*4882a593Smuzhiyun pDev->mpIntf->cur_altsetting->desc.bInterfaceNumber,
4127*4882a593Smuzhiyun NULL,
4128*4882a593Smuzhiyun 0,
4129*4882a593Smuzhiyun 100 );
4130*4882a593Smuzhiyun if (result < 0)
4131*4882a593Smuzhiyun {
4132*4882a593Smuzhiyun DBG( "Bad SetControlLineState status %d\n", result );
4133*4882a593Smuzhiyun return result;
4134*4882a593Smuzhiyun }
4135*4882a593Smuzhiyun
4136*4882a593Smuzhiyun return 0;
4137*4882a593Smuzhiyun }
4138*4882a593Smuzhiyun
4139*4882a593Smuzhiyun /*===========================================================================
4140*4882a593Smuzhiyun METHOD:
4141*4882a593Smuzhiyun QMIDMSGetMEID (Public Method)
4142*4882a593Smuzhiyun
4143*4882a593Smuzhiyun DESCRIPTION:
4144*4882a593Smuzhiyun Register DMS client
4145*4882a593Smuzhiyun send MEID req and parse response
4146*4882a593Smuzhiyun Release DMS client
4147*4882a593Smuzhiyun
4148*4882a593Smuzhiyun PARAMETERS:
4149*4882a593Smuzhiyun pDev [ I ] - Device specific memory
4150*4882a593Smuzhiyun
4151*4882a593Smuzhiyun RETURN VALUE:
4152*4882a593Smuzhiyun None
4153*4882a593Smuzhiyun ===========================================================================*/
4154*4882a593Smuzhiyun static int QMIDMSGetMEID( sGobiUSBNet * pDev )
4155*4882a593Smuzhiyun {
4156*4882a593Smuzhiyun int result;
4157*4882a593Smuzhiyun void * pWriteBuffer;
4158*4882a593Smuzhiyun u16 writeBufferSize;
4159*4882a593Smuzhiyun void * pReadBuffer;
4160*4882a593Smuzhiyun u16 readBufferSize;
4161*4882a593Smuzhiyun u16 DMSClientID;
4162*4882a593Smuzhiyun
4163*4882a593Smuzhiyun if (IsDeviceValid( pDev ) == false)
4164*4882a593Smuzhiyun {
4165*4882a593Smuzhiyun DBG( "Invalid device\n" );
4166*4882a593Smuzhiyun return -EFAULT;
4167*4882a593Smuzhiyun }
4168*4882a593Smuzhiyun
4169*4882a593Smuzhiyun result = GetClientID( pDev, QMIDMS );
4170*4882a593Smuzhiyun if (result < 0)
4171*4882a593Smuzhiyun {
4172*4882a593Smuzhiyun return result;
4173*4882a593Smuzhiyun }
4174*4882a593Smuzhiyun DMSClientID = result;
4175*4882a593Smuzhiyun
4176*4882a593Smuzhiyun // QMI DMS Get Serial numbers Req
4177*4882a593Smuzhiyun writeBufferSize = QMIDMSGetMEIDReqSize();
4178*4882a593Smuzhiyun pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL );
4179*4882a593Smuzhiyun if (pWriteBuffer == NULL)
4180*4882a593Smuzhiyun {
4181*4882a593Smuzhiyun return -ENOMEM;
4182*4882a593Smuzhiyun }
4183*4882a593Smuzhiyun
4184*4882a593Smuzhiyun result = QMIDMSGetMEIDReq( pWriteBuffer,
4185*4882a593Smuzhiyun writeBufferSize,
4186*4882a593Smuzhiyun 1 );
4187*4882a593Smuzhiyun if (result < 0)
4188*4882a593Smuzhiyun {
4189*4882a593Smuzhiyun kfree( pWriteBuffer );
4190*4882a593Smuzhiyun return result;
4191*4882a593Smuzhiyun }
4192*4882a593Smuzhiyun
4193*4882a593Smuzhiyun result = WriteSync( pDev,
4194*4882a593Smuzhiyun pWriteBuffer,
4195*4882a593Smuzhiyun writeBufferSize,
4196*4882a593Smuzhiyun DMSClientID );
4197*4882a593Smuzhiyun kfree( pWriteBuffer );
4198*4882a593Smuzhiyun
4199*4882a593Smuzhiyun if (result < 0)
4200*4882a593Smuzhiyun {
4201*4882a593Smuzhiyun return result;
4202*4882a593Smuzhiyun }
4203*4882a593Smuzhiyun
4204*4882a593Smuzhiyun // QMI DMS Get Serial numbers Resp
4205*4882a593Smuzhiyun result = ReadSync( pDev,
4206*4882a593Smuzhiyun &pReadBuffer,
4207*4882a593Smuzhiyun DMSClientID,
4208*4882a593Smuzhiyun 1 );
4209*4882a593Smuzhiyun if (result < 0)
4210*4882a593Smuzhiyun {
4211*4882a593Smuzhiyun return result;
4212*4882a593Smuzhiyun }
4213*4882a593Smuzhiyun readBufferSize = result;
4214*4882a593Smuzhiyun
4215*4882a593Smuzhiyun result = QMIDMSGetMEIDResp( pReadBuffer,
4216*4882a593Smuzhiyun readBufferSize,
4217*4882a593Smuzhiyun &pDev->mMEID[0],
4218*4882a593Smuzhiyun 14 );
4219*4882a593Smuzhiyun kfree( pReadBuffer );
4220*4882a593Smuzhiyun
4221*4882a593Smuzhiyun if (result < 0)
4222*4882a593Smuzhiyun {
4223*4882a593Smuzhiyun DBG( "bad get MEID resp\n" );
4224*4882a593Smuzhiyun
4225*4882a593Smuzhiyun // Non fatal error, device did not return any MEID
4226*4882a593Smuzhiyun // Fill with 0's
4227*4882a593Smuzhiyun memset( &pDev->mMEID[0], '0', 14 );
4228*4882a593Smuzhiyun }
4229*4882a593Smuzhiyun
4230*4882a593Smuzhiyun ReleaseClientID( pDev, DMSClientID );
4231*4882a593Smuzhiyun
4232*4882a593Smuzhiyun // Success
4233*4882a593Smuzhiyun return 0;
4234*4882a593Smuzhiyun }
4235*4882a593Smuzhiyun
4236*4882a593Smuzhiyun /*===========================================================================
4237*4882a593Smuzhiyun METHOD:
4238*4882a593Smuzhiyun QMIWDASetDataFormat (Public Method)
4239*4882a593Smuzhiyun
4240*4882a593Smuzhiyun DESCRIPTION:
4241*4882a593Smuzhiyun Register WDA client
4242*4882a593Smuzhiyun send Data format request and parse response
4243*4882a593Smuzhiyun Release WDA client
4244*4882a593Smuzhiyun
4245*4882a593Smuzhiyun PARAMETERS:
4246*4882a593Smuzhiyun pDev [ I ] - Device specific memory
4247*4882a593Smuzhiyun
4248*4882a593Smuzhiyun RETURN VALUE:
4249*4882a593Smuzhiyun None
4250*4882a593Smuzhiyun ===========================================================================*/
4251*4882a593Smuzhiyun static int QMIWDASetDataFormat( sGobiUSBNet * pDev, int qmap_mode, int *rx_urb_size )
4252*4882a593Smuzhiyun {
4253*4882a593Smuzhiyun int result;
4254*4882a593Smuzhiyun void * pWriteBuffer;
4255*4882a593Smuzhiyun u16 writeBufferSize;
4256*4882a593Smuzhiyun void * pReadBuffer;
4257*4882a593Smuzhiyun u16 readBufferSize;
4258*4882a593Smuzhiyun u16 WDAClientID;
4259*4882a593Smuzhiyun
4260*4882a593Smuzhiyun DBG("\n");
4261*4882a593Smuzhiyun
4262*4882a593Smuzhiyun if (IsDeviceValid( pDev ) == false)
4263*4882a593Smuzhiyun {
4264*4882a593Smuzhiyun DBG( "Invalid device\n" );
4265*4882a593Smuzhiyun return -EFAULT;
4266*4882a593Smuzhiyun }
4267*4882a593Smuzhiyun
4268*4882a593Smuzhiyun result = GetClientID( pDev, QMIWDA );
4269*4882a593Smuzhiyun if (result < 0)
4270*4882a593Smuzhiyun {
4271*4882a593Smuzhiyun return result;
4272*4882a593Smuzhiyun }
4273*4882a593Smuzhiyun WDAClientID = result;
4274*4882a593Smuzhiyun
4275*4882a593Smuzhiyun // QMI WDA Set Data Format Request
4276*4882a593Smuzhiyun writeBufferSize = QMIWDASetDataFormatReqSize(qmap_mode);
4277*4882a593Smuzhiyun pWriteBuffer = kmalloc( writeBufferSize, GFP_KERNEL );
4278*4882a593Smuzhiyun if (pWriteBuffer == NULL)
4279*4882a593Smuzhiyun {
4280*4882a593Smuzhiyun return -ENOMEM;
4281*4882a593Smuzhiyun }
4282*4882a593Smuzhiyun
4283*4882a593Smuzhiyun result = QMIWDASetDataFormatReq( pWriteBuffer,
4284*4882a593Smuzhiyun writeBufferSize, pDev->mbRawIPMode,
4285*4882a593Smuzhiyun qmap_mode ? pDev->qmap_version : 0, (31*1024),
4286*4882a593Smuzhiyun 1 );
4287*4882a593Smuzhiyun
4288*4882a593Smuzhiyun if (result < 0)
4289*4882a593Smuzhiyun {
4290*4882a593Smuzhiyun kfree( pWriteBuffer );
4291*4882a593Smuzhiyun return result;
4292*4882a593Smuzhiyun }
4293*4882a593Smuzhiyun
4294*4882a593Smuzhiyun result = WriteSync( pDev,
4295*4882a593Smuzhiyun pWriteBuffer,
4296*4882a593Smuzhiyun writeBufferSize,
4297*4882a593Smuzhiyun WDAClientID );
4298*4882a593Smuzhiyun kfree( pWriteBuffer );
4299*4882a593Smuzhiyun
4300*4882a593Smuzhiyun if (result < 0)
4301*4882a593Smuzhiyun {
4302*4882a593Smuzhiyun return result;
4303*4882a593Smuzhiyun }
4304*4882a593Smuzhiyun
4305*4882a593Smuzhiyun // QMI DMS Get Serial numbers Resp
4306*4882a593Smuzhiyun result = ReadSync( pDev,
4307*4882a593Smuzhiyun &pReadBuffer,
4308*4882a593Smuzhiyun WDAClientID,
4309*4882a593Smuzhiyun 1 );
4310*4882a593Smuzhiyun if (result < 0)
4311*4882a593Smuzhiyun {
4312*4882a593Smuzhiyun return result;
4313*4882a593Smuzhiyun }
4314*4882a593Smuzhiyun readBufferSize = result;
4315*4882a593Smuzhiyun
4316*4882a593Smuzhiyun if (qmap_mode && rx_urb_size) {
4317*4882a593Smuzhiyun int qmap_version = 0, rx_size = 0, tx_size = 0;
4318*4882a593Smuzhiyun result = QMIWDASetDataFormatResp( pReadBuffer,
4319*4882a593Smuzhiyun readBufferSize, pDev->mbRawIPMode, &qmap_version, &rx_size, &tx_size, &pDev->qmap_settings);
4320*4882a593Smuzhiyun INFO( "qmap settings qmap_version=%d, rx_size=%d, tx_size=%d\n",
4321*4882a593Smuzhiyun le32_to_cpu(qmap_version), le32_to_cpu(rx_size), le32_to_cpu(tx_size));
4322*4882a593Smuzhiyun
4323*4882a593Smuzhiyun if (le32_to_cpu(qmap_version)) {
4324*4882a593Smuzhiyun #if defined(QUECTEL_UL_DATA_AGG)
4325*4882a593Smuzhiyun struct ul_agg_ctx *ctx = &pDev->agg_ctx;
4326*4882a593Smuzhiyun
4327*4882a593Smuzhiyun if (le32_to_cpu(pDev->qmap_settings.ul_data_aggregation_max_datagrams) > 1) {
4328*4882a593Smuzhiyun ctx->ul_data_aggregation_max_size = le32_to_cpu(pDev->qmap_settings.ul_data_aggregation_max_size);
4329*4882a593Smuzhiyun ctx->ul_data_aggregation_max_datagrams = le32_to_cpu(pDev->qmap_settings.ul_data_aggregation_max_datagrams);
4330*4882a593Smuzhiyun ctx->dl_minimum_padding = le32_to_cpu(pDev->qmap_settings.dl_minimum_padding);
4331*4882a593Smuzhiyun }
4332*4882a593Smuzhiyun INFO( "qmap settings ul_data_aggregation_max_size=%d, ul_data_aggregation_max_datagrams=%d\n",
4333*4882a593Smuzhiyun ctx->ul_data_aggregation_max_size, ctx->ul_data_aggregation_max_datagrams);
4334*4882a593Smuzhiyun if (ctx->ul_data_aggregation_max_datagrams > 11)
4335*4882a593Smuzhiyun ctx->ul_data_aggregation_max_datagrams = 11;
4336*4882a593Smuzhiyun #endif
4337*4882a593Smuzhiyun *rx_urb_size = le32_to_cpu(rx_size);
4338*4882a593Smuzhiyun } else {
4339*4882a593Smuzhiyun *rx_urb_size = 0;
4340*4882a593Smuzhiyun result = -EFAULT;
4341*4882a593Smuzhiyun }
4342*4882a593Smuzhiyun } else {
4343*4882a593Smuzhiyun int qmap_enabled = 0, rx_size = 0, tx_size = 0;
4344*4882a593Smuzhiyun result = QMIWDASetDataFormatResp( pReadBuffer,
4345*4882a593Smuzhiyun readBufferSize, pDev->mbRawIPMode, &qmap_enabled, &rx_size, &tx_size, NULL);
4346*4882a593Smuzhiyun }
4347*4882a593Smuzhiyun
4348*4882a593Smuzhiyun kfree( pReadBuffer );
4349*4882a593Smuzhiyun
4350*4882a593Smuzhiyun if (result < 0)
4351*4882a593Smuzhiyun {
4352*4882a593Smuzhiyun DBG( "Data Format Cannot be set\n" );
4353*4882a593Smuzhiyun }
4354*4882a593Smuzhiyun
4355*4882a593Smuzhiyun ReleaseClientID( pDev, WDAClientID );
4356*4882a593Smuzhiyun
4357*4882a593Smuzhiyun // Success
4358*4882a593Smuzhiyun return 0;
4359*4882a593Smuzhiyun }
4360*4882a593Smuzhiyun
4361*4882a593Smuzhiyun int QuecQMIWDASetDataFormat( sGobiUSBNet * pDev, int qmap_mode, int *rx_urb_size ) {
4362*4882a593Smuzhiyun return QMIWDASetDataFormat(pDev, qmap_mode, rx_urb_size);
4363*4882a593Smuzhiyun }
4364