xref: /OK3568_Linux_fs/kernel/drivers/net/usb/GobiNet/QMIDevice.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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