aboutsummaryrefslogtreecommitdiff
path: root/drivers/sdio/stack/busdriver/sdio_bus_os.c
blob: c6f68e62159da950953789e8ae03e8e33bfb2ddf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@file: sdio_bus_os.c

@abstract: Linux implementation module

#notes: includes module load and unload functions

@notice: Copyright (c), 2004-2006 Atheros Communications, Inc.


 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation;
 *
 *  Software distributed under the License is distributed on an "AS
 *  IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 *  implied. See the License for the specific language governing
 *  rights and limitations under the License.
 *
 *  Portions of this code were developed with information supplied from the
 *  SD Card Association Simplified Specifications. The following conditions and disclaimers may apply:
 *
 *   The following conditions apply to the release of the SD simplified specification (�Simplified
 *   Specification�) by the SD Card Association. The Simplified Specification is a subset of the complete
 *   SD Specification which is owned by the SD Card Association. This Simplified Specification is provided
 *   on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified
 *   Specification may require a license from the SD Card Association or other third parties.
 *   Disclaimers:
 *   The information contained in the Simplified Specification is presented only as a standard
 *   specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any
 *   representations or warranties of any kind. No responsibility is assumed by the SD Card Association for
 *   any damages, any infringements of patents or other right of the SD Card Association or any third
 *   parties, which may result from its use. No license is granted by implication, estoppel or otherwise
 *   under any patent or other rights of the SD Card Association or any third party. Nothing herein shall
 *   be construed as an obligation by the SD Card Association to disclose or distribute any technical
 *   information, know-how or other confidential information to any third party.
 *
 *
 *  The initial developers of the original code are Seung Yi and Paul Lever
 *
 *  sdio@atheros.com
 *
 *

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* debug level for this module*/
#define DBG_DECLARE 3;

#include <linux/sdio/ctsystem.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/pnp.h>
void pnp_remove_card_device(struct pnp_dev *dev);
#include <linux/sdio/sdio_busdriver.h>
#include <linux/sdio/sdio_lib.h>
#include "_busdriver.h"
/* new for 2.6.26-rc1 --- not sure this is a great way... */
#include "../../../pnp/base.h"

#define DESCRIPTION "SDIO Bus Driver"
#define AUTHOR "Atheros Communications, Inc."

/* debug print parameter */
/* configuration and default parameters */
static int RequestRetries = SDMMC_DEFAULT_CMD_RETRIES;
module_param(RequestRetries, int, 0644);
MODULE_PARM_DESC(RequestRetries, "number of command retries");
static int CardReadyPollingRetry = SDMMC_DEFAULT_CARD_READY_RETRIES;
module_param(CardReadyPollingRetry, int, 0644);
MODULE_PARM_DESC(CardReadyPollingRetry, "number of card ready retries");
static int PowerSettleDelay = SDMMC_POWER_SETTLE_DELAY;
module_param(PowerSettleDelay, int, 0644);
MODULE_PARM_DESC(PowerSettleDelay, "delay in ms for power to settle after power changes");
static int DefaultOperClock = 52000000;
module_param(DefaultOperClock, int, 0644);
MODULE_PARM_DESC(DefaultOperClock, "maximum operational clock limit");
static int DefaultBusMode = SDCONFIG_BUS_WIDTH_4_BIT;
module_param(DefaultBusMode, int, 0644);
MODULE_PARM_DESC(DefaultBusMode, "default bus mode: see SDCONFIG_BUS_WIDTH_xxx");
static int RequestListSize = SDBUS_DEFAULT_REQ_LIST_SIZE;
module_param(RequestListSize, int, 0644);
MODULE_PARM_DESC(RequestListSize, "");
static int SignalSemListSize = SDBUS_DEFAULT_REQ_SIG_SIZE;
module_param(SignalSemListSize, int, 0644);
MODULE_PARM_DESC(SignalSemListSize, "");
static int CDPollingInterval = SDBUS_DEFAULT_CD_POLLING_INTERVAL;
module_param(CDPollingInterval, int, 0644);
MODULE_PARM_DESC(CDPollingInterval, "");
static int DefaultOperBlockLen = SDMMC_DEFAULT_BYTES_PER_BLOCK;
module_param(DefaultOperBlockLen, int, 0644);
MODULE_PARM_DESC(DefaultOperBlockLen, "operational block length");
static int DefaultOperBlockCount = SDMMC_DEFAULT_BLOCKS_PER_TRANS;
module_param(DefaultOperBlockCount, int, 0644);
MODULE_PARM_DESC(DefaultOperBlockCount, "operational block count");
static int ConfigFlags = BD_DEFAULT_CONFIG_FLAGS;
module_param(ConfigFlags, int, 0644);
MODULE_PARM_DESC(ConfigFlags, "config flags");

static int HcdRCount = MAX_HCD_REQ_RECURSION;
module_param(HcdRCount, int, 0644);
MODULE_PARM_DESC(HcdRCount, "HCD request recursion count");

static void CardDetect_WorkItem(
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
void *context);
#else
struct work_struct *ignored);
#endif
static void CardDetect_TimerFunc(unsigned long Context);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
static DECLARE_WORK(CardDetectPollWork, CardDetect_WorkItem
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
, 0);
#else
);
#endif
#endif
static int RegisterDriver(PSDFUNCTION pFunction);
static int UnregisterDriver(PSDFUNCTION pFunction);

static struct timer_list CardDetectTimer;

#define SDDEVICE_FROM_OSDEVICE(pOSDevice)  container_of(pOSDevice, SDDEVICE, Device)
#define SDFUNCTION_FROM_OSDRIVER(pOSDriver)  container_of(pOSDriver, SDFUNCTION, Driver)


/*
 * SDIO_RegisterHostController - register a host controller bus driver
*/
SDIO_STATUS SDIO_RegisterHostController(PSDHCD pHcd) {
    /* we are the exported verison, call the internal verison */
    return _SDIO_RegisterHostController(pHcd);
}

/*
 * SDIO_UnregisterHostController - unregister a host controller bus driver
*/
SDIO_STATUS SDIO_UnregisterHostController(PSDHCD pHcd) {
    /* we are the exported verison, call the internal verison */
    return _SDIO_UnregisterHostController(pHcd);
}

/*
 * SDIO_RegisterFunction - register a function driver
*/
SDIO_STATUS SDIO_RegisterFunction(PSDFUNCTION pFunction) {
    int error;
    SDIO_STATUS status;

    DBG_PRINT(SDDBG_TRACE, ("SDIO BusDriver - SDIO_RegisterFunction\n"));

        /* since we do PnP registration first, we need to check the version */
    if (!CHECK_FUNCTION_DRIVER_VERSION(pFunction)) {
        DBG_PRINT(SDDBG_ERROR,
           ("SDIO Bus Driver: Function Major Version Mismatch (hcd = %d, bus driver = %d)\n",
           GET_SDIO_STACK_VERSION_MAJOR(pFunction), CT_SDIO_STACK_VERSION_MAJOR(g_Version)));
        return SDIO_STATUS_INVALID_PARAMETER;
    }

    /* we are the exported verison, call the internal verison after registering with the bus
       we handle probes internally to the bus driver */
    if ((error = RegisterDriver(pFunction)) < 0) {
        DBG_PRINT(SDDBG_ERROR,
            ("SDIO BusDriver - SDIO_RegisterFunction, failed to register with system bus driver: %d\n",
            error));
        status = OSErrorToSDIOError(error);
    } else {
        status = _SDIO_RegisterFunction(pFunction);
        if (!SDIO_SUCCESS(status)) {
            UnregisterDriver(pFunction);
        }
    }

    return status;
}

/*
 * SDIO_UnregisterFunction - unregister a function driver
*/
SDIO_STATUS SDIO_UnregisterFunction(PSDFUNCTION pFunction) {
    SDIO_STATUS status;
    /* we are the exported verison, call the internal verison */
    status = _SDIO_UnregisterFunction(pFunction);
    UnregisterDriver(pFunction);
    return  status;
}

/*
 * SDIO_HandleHcdEvent - tell core an event occurred
*/
SDIO_STATUS SDIO_HandleHcdEvent(PSDHCD pHcd, HCD_EVENT Event) {
    /* we are the exported verison, call the internal verison */
    DBG_PRINT(SDIODBG_HCD_EVENTS, ("SDIO Bus Driver: SDIO_HandleHcdEvent, event type 0x%X, HCD:0x%X\n",
                         Event, (UINT)pHcd));
    return _SDIO_HandleHcdEvent(pHcd, Event);
}

/* get default settings */
SDIO_STATUS _SDIO_BusGetDefaultSettings(PBDCONTEXT pBdc)
{
    /* these defaults are module params */
    pBdc->RequestRetries = RequestRetries;
    pBdc->CardReadyPollingRetry = CardReadyPollingRetry;
    pBdc->PowerSettleDelay = PowerSettleDelay;
    pBdc->DefaultOperClock = DefaultOperClock;
    pBdc->DefaultBusMode = DefaultBusMode;
    pBdc->RequestListSize = RequestListSize;
    pBdc->SignalSemListSize = SignalSemListSize;
    pBdc->CDPollingInterval = CDPollingInterval;
    pBdc->DefaultOperBlockLen = DefaultOperBlockLen;
    pBdc->DefaultOperBlockCount = DefaultOperBlockCount;
    pBdc->ConfigFlags = ConfigFlags;
    pBdc->MaxHcdRecursion = HcdRCount;
    return SDIO_STATUS_SUCCESS;
}

static void CardDetect_TimerFunc(unsigned long Context)
{
    DBG_PRINT(SDIODBG_CD_TIMER, ("+ SDIO BusDriver Card Detect Timer\n"));

        /* timers run in an ISR context and cannot block or sleep, so we need
         * to queue a work item to call the bus driver timer notification */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
    if (schedule_work(&CardDetectPollWork) <= 0) {
        DBG_PRINT(SDDBG_ERROR, ("Failed to queue Card Detect timer!\n"));
    }
#else
    CardDetect_WorkItem(NULL);
#endif
    DBG_PRINT(SDIODBG_CD_TIMER, ("- SDIO BusDriver  Card Detect Timer\n"));
}

/*
 * Initialize any timers we are using
*/
SDIO_STATUS InitializeTimers(void)
{
    init_timer(&CardDetectTimer);
    CardDetectTimer.function = CardDetect_TimerFunc;
    CardDetectTimer.data = 0;
    return SDIO_STATUS_SUCCESS;
}

/*
 * cleanup timers
*/
SDIO_STATUS CleanupTimers(void)
{
    del_timer(&CardDetectTimer);
    return SDIO_STATUS_SUCCESS;
}


/*
 * Queue a timer, Timeout is in milliseconds
*/
SDIO_STATUS QueueTimer(INT TimerID, UINT32 TimeOut)
{
    UINT32 delta;

        /* convert timeout to ticks */
    delta = (TimeOut * HZ)/1000;
    if (delta == 0) {
        delta = 1;
    }
    DBG_PRINT(SDIODBG_CD_TIMER, ("SDIO BusDriver - SDIO_QueueTimer System Ticks Per Sec:%d \n",HZ));
    DBG_PRINT(SDIODBG_CD_TIMER, ("SDIO BusDriver - SDIO_QueueTimer TimerID: %d TimeOut:%d MS, requires %d Ticks\n",
                TimerID,TimeOut,delta));
    switch (TimerID) {
        case SDIOBUS_CD_TIMER_ID:
            CardDetectTimer.expires = jiffies + delta;
            add_timer(&CardDetectTimer);
            break;
        default:
            return SDIO_STATUS_INVALID_PARAMETER;
    }

    return SDIO_STATUS_SUCCESS;
}

/* check a response on behalf of the host controller, to allow it to proceed with a
 * data transfer */
SDIO_STATUS SDIO_CheckResponse(PSDHCD pHcd, PSDREQUEST pReq, SDHCD_RESPONSE_CHECK_MODE CheckMode)
{
    return _SDIO_CheckResponse(pHcd,pReq,CheckMode);
}

/*
 * CardDetect_WorkItem - the work item for handling card detect polling interrupt
*/
static void CardDetect_WorkItem(
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
void *context)
#else
struct work_struct *ignored)
#endif
{
        /* call bus driver function */
    SDIO_NotifyTimerTriggered(SDIOBUS_CD_TIMER_ID);
}

/*
 * OS_IncHcdReference - increment host controller driver reference count
*/
SDIO_STATUS Do_OS_IncHcdReference(PSDHCD pHcd)
{
    SDIO_STATUS status = SDIO_STATUS_SUCCESS;

    do {
        if (NULL == pHcd->pModule) {
                /* hcds that are 2.3 or higher should set this */
            DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: HCD:%s should set module ptr!\n",
                (pHcd->pName != NULL) ? pHcd->pName : "Unknown"));
            break;
        }

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
        if (!try_module_get(pHcd->pModule)) {
            status = SDIO_STATUS_ERROR;
        }
#else
        if (!try_inc_mod_count(pHcd->pModule)) {
            status = SDIO_STATUS_ERROR;
        }
#endif

    } while (FALSE);

    if (!SDIO_SUCCESS(status)) {
        DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: HCD:%s failed to get module\n",
            (pHcd->pName != NULL) ? pHcd->pName : "Unknown"));
    }

    return status;
}

/*
 * OS_DecHcdReference - decrement host controller driver reference count
*/
SDIO_STATUS Do_OS_DecHcdReference(PSDHCD pHcd)
{
    if (pHcd->pModule != NULL) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
        module_put(pHcd->pModule);
#else
            /* 2.4 or lower */
        __MOD_DEC_USE_COUNT(pHcd->pModule);
#endif
    }
    return SDIO_STATUS_SUCCESS;
}

/****************************************************************************************/

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
#include <linux/pnp.h>

#if !defined(CONFIG_PNP)
#error "CONFIG_PNP not defined"
#endif

static ULONG InUseDevices = 0;
static spinlock_t InUseDevicesLock = SPIN_LOCK_UNLOCKED;

static const struct pnp_device_id pnp_idtable[] = {
    {"SD_XXXX",  0}
};
static int sdio_get_resources(struct pnp_dev * pDev)
{
    DBG_PRINT(SDDBG_TRACE,
        ("SDIO BusDriver - sdio_get_resources: %s\n",
        pDev->dev.bus_id));
    return 0;
}
static int sdio_set_resources(struct pnp_dev * pDev)
{
    DBG_PRINT(SDDBG_TRACE,
        ("SDIO BusDriver - sdio_set_resources: %s\n",
        pDev->dev.bus_id));
    return 0;
}

static int sdio_disable_resources(struct pnp_dev *pDev)
{
    DBG_PRINT(SDDBG_TRACE,
        ("SDIO BusDriver - sdio_disable_resources: %s\n",
        pDev->dev.bus_id));
    if (pDev != NULL) {
        pDev->active = 0;
    }
    return 0;
}
void    release(struct device * pDev) {
    DBG_PRINT(SDDBG_TRACE,
        ("SDIO BusDriver - release: %s\n",
        pDev->bus_id));
    return;
}
struct pnp_protocol sdio_protocol = {
    .name   = "SDIO",
    .get    = sdio_get_resources,
    .set    = sdio_set_resources,
    .disable = sdio_disable_resources,
    .dev.release = release,
};

/*
 * driver_probe - probe for OS based driver
*/
static int driver_probe(struct pnp_dev* pOSDevice, const struct pnp_device_id *pId)
{
    PSDDEVICE pDevice = SDDEVICE_FROM_OSDEVICE(&pOSDevice);
    PSDFUNCTION pFunction = pDevice->Device->dev.driver_data;

    if (pFunction == NULL) {
        return -1;
    }

    if (strcmp(pFunction->pName, pOSDevice->dev.driver->name) == 0) {
        DBG_PRINT(SDDBG_TRACE,
            ("SDIO BusDriver - driver_probe, match: %s/%s driver: %s\n",
            pOSDevice->dev.bus_id, pFunction->pName, pOSDevice->dev.driver->name));
        return 1;
    } else {
        DBG_PRINT(SDDBG_TRACE,
            ("SDIO BusDriver - driver_probe, no match: %s/%s driver: %s\n",
            pOSDevice->dev.bus_id, pFunction->pName, pOSDevice->dev.driver->name));
        return -1;
    }
/*    if (pOSDevice->id != NULL) {
        if (strcmp(pOSDevice->id->id, pId->id) == 0) {
            DBG_PRINT(SDDBG_TRACE,
                ("SDIO BusDriver - driver_probe, match: %s/%s\n",
                pOSDevice->dev.bus_id, pId->id));
            return 1;
        }
        DBG_PRINT(SDDBG_TRACE,
            ("SDIO BusDriver - driver_probe, did not match: %s/%s/%s\n",
            pOSDevice->dev.bus_id, pId->id, pOSDevice->id->id));
    } else {
        DBG_PRINT(SDDBG_TRACE,
            ("SDIO BusDriver - driver_probe, did not match: %s/%s\n",
            pOSDevice->dev.bus_id, pId->id));
    }
    return -1;
*/
//??    if (pDevice->Device.dev.driver_data != NULL) {
//??        if (pDevice->Device.dev.driver_data == pFunction) {
//??    if (pDevice->Device.data != NULL) {
//??        if (pDevice->Device.data == pFunction) {
//??            DBG_PRINT(SDDBG_TRACE,
//??                ("SDIO BusDriver - driver_probe, match: %s\n",
//??                pOSDevice->dev.bus_id));
//??            return 1;
//??        }
//??    }
   DBG_PRINT(SDDBG_TRACE,
        ("SDIO BusDriver - driver_probe,  match: %s\n",
        pOSDevice->dev.bus_id));
    return 1;
}

static int RegisterDriver(PSDFUNCTION pFunction)
{
    memset(&pFunction->Driver, 0, sizeof(pFunction->Driver));
    pFunction->Driver.name = pFunction->pName;
    pFunction->Driver.probe = driver_probe;
    pFunction->Driver.id_table = pnp_idtable;
    pFunction->Driver.flags = PNP_DRIVER_RES_DO_NOT_CHANGE;

    DBG_PRINT(SDDBG_TRACE,
            ("SDIO BusDriver - SDIO_RegisterFunction, registering driver: %s\n",
            pFunction->Driver.name));
    return pnp_register_driver(&pFunction->Driver);
}

static int UnregisterDriver(PSDFUNCTION pFunction)
{
    DBG_PRINT(SDDBG_TRACE,
            ("+SDIO BusDriver - UnregisterDriver, driver: %s\n",
            pFunction->Driver.name));
    pnp_unregister_driver(&pFunction->Driver);
    DBG_PRINT(SDDBG_TRACE,
            ("-SDIO BusDriver - UnregisterDriver\n"));
   return 0;
}

/*
 * OS_InitializeDevice - initialize device that will be registered
*/
SDIO_STATUS OS_InitializeDevice(PSDDEVICE pDevice, PSDFUNCTION pFunction)
{
    char id_name[20];

    /* set the id as slot number/function number */
    snprintf(id_name, sizeof(id_name) - 1, "SD_%02X%02X",
             pDevice->pHcd->SlotNumber, (UINT)SDDEVICE_GET_SDIO_FUNCNO(pDevice));

    pDevice->Device = pnp_alloc_dev(&sdio_protocol, pDevice->pHcd->SlotNumber + 1, id_name);
    pDevice->Device->dev.driver_data = (PVOID)pFunction;
//??    pDevice->Device.data = (PVOID)pFunction;
//??    pDevice->Device.dev.driver = &pFunction->Driver.driver;
//??    pDevice->Device.driver = &pFunction->Driver;
//??    pDevice->Device.dev.release = release;
    /* get a unique device number, must be done with locks held */
    spin_lock(&InUseDevicesLock);
    pDevice->Device->number = FirstClearBit(&InUseDevices);
    SetBit(&InUseDevices, pDevice->Device->number);
    spin_unlock(&InUseDevicesLock);
    pDevice->Device->capabilities = PNP_REMOVABLE | PNP_DISABLE;
    pDevice->Device->active = 1;

    DBG_PRINT(SDDBG_TRACE, ("SDIO BusDriver - OS_InitializeDevice adding id: %s\n",
                             id_name));
    /* deal with DMA settings */
    if (pDevice->pHcd->pDmaDescription != NULL) {
        pDevice->Device->dev.dma_mask = &pDevice->pHcd->pDmaDescription->Mask;
        pDevice->Device->dev.coherent_dma_mask = pDevice->pHcd->pDmaDescription->Mask;
    }

    return SDIO_STATUS_SUCCESS;
}

/*
 * OS_AddDevice - must be pre-initialized with OS_InitializeDevice
*/
SDIO_STATUS OS_AddDevice(PSDDEVICE pDevice, PSDFUNCTION pFunction)
{
    int error;
    DBG_PRINT(SDDBG_TRACE, ("SDIO BusDriver - OS_AddDevice adding function: %s\n",
                               pFunction->pName));
    error = pnp_add_device(pDevice->Device);
    if (error < 0) {
        DBG_PRINT(SDDBG_ERROR, ("SDIO BusDriver - OS_AddDevice failed pnp_add_device: %d\n",
                               error));
    }
        /* replace the buggy pnp's release */
    pDevice->Device->dev.release = release;

    return OSErrorToSDIOError(error);
}

/*
 * OS_RemoveDevice - unregister device with driver and bus
*/
void OS_RemoveDevice(PSDDEVICE pDevice)
{
    DBG_PRINT(SDDBG_TRACE, ("SDIO BusDriver - OS_RemoveDevice \n"));
    pnp_remove_card_device(pDevice->Device);
    spin_lock(&InUseDevicesLock);
    ClearBit(&InUseDevices, pDevice->Device->number);
    spin_unlock(&InUseDevicesLock);

    if (pDevice->Device->id != NULL) {
        KernelFree(pDevice->Device->id);
        pDevice->Device->id = NULL;
    }

    KernelFree(pDevice->Device);
}

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  @function: Add OS device to bus driver.

  @function name: SDIO_BusAddOSDevice
  @category: HD_Reference

  @output: pDma    - descrip[tion of support DMA or NULL
  @output: pDriver - assigned driver object
  @output: pDevice - assigned device object

  @return: SDIO_STATUS - SDIO_STATUS_SUCCESS when successful.

  @notes: If the HCD does not register with the driver sub-system directly (like in the PCI case),
          then it should register with the bus driver to obtain OS dependent device objects.
          All input structures should be maintained throughout the life of the driver.

  @example: getting device objects:
    typedef struct _SDHCD_DRIVER {
        OS_PNPDEVICE   HcdDevice;     / * the OS device for this HCD * /
        OS_PNPDRIVER   HcdDriver;     / * the OS driver for this HCD * /
        SDDMA_DESCRIPTION Dma;        / * driver DMA description * /
    }SDHCD_DRIVER, *PSDHCD_DRIVER;

    typedef struct _SDHCD_DRIVER_CONTEXT {
        PTEXT        pDescription;       / * human readable device decsription * /
        SDLIST       DeviceList;         / * the list of current devices handled by this driver * /
        OS_SEMAPHORE DeviceListSem;      / * protection for the DeviceList * /
        UINT         DeviceCount;        / * number of devices currently installed * /
        SDHCD_DRIVER Driver;             / * OS dependent driver specific info * /
    }SDHCD_DRIVER_CONTEXT, *PSDHCD_DRIVER_CONTEXT;

    static SDHCD_DRIVER_CONTEXT HcdContext = {
        .pDescription  = DESCRIPTION,
        .DeviceCount   = 0,
        .Driver.HcdDevice.name = "sdio_xxx_hcd",
        .Driver.HcdDriver.name = "sdio_xxx_hcd",
    }
    .....
    status = SDIO_BusAddOSDevice(NULL, &HcdContext.Driver, &HcdContext.Device);
    if (SDIO_SUCCESS(status) {
        return Probe(&HcdContext.Device);
    }
    return SDIOErrorToOSError(status);

  @see also: SDIO_BusRemoveOSDevice

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
SDIO_STATUS SDIO_BusAddOSDevice(PSDDMA_DESCRIPTION pDma, POS_PNPDRIVER pDriver, POS_PNPDEVICE *outDevice, const char* name)
{
    int err;
    struct pnp_device_id *pFdid;
    static int slotNumber = 0; /* we just use an increasing count for the slots number */
    struct pnp_dev* pDevice;

    pFdid = KernelAlloc(sizeof(struct pnp_device_id)*2);
    /* set the id as slot number/function number */
    snprintf(pFdid[0].id, sizeof(pFdid[0].id), "SD_%02X08",
             slotNumber++);
    pFdid[0].driver_data = 0;
    pFdid[1].id[0] = '\0';
    pFdid[1].driver_data = 0;

    pDriver->id_table = pFdid;
    pDriver->flags = PNP_DRIVER_RES_DO_NOT_CHANGE;
    err = pnp_register_driver(pDriver);
    if (err < 0) {
        DBG_PRINT(SDDBG_ERROR,
            ("SDIO BusDriver - SDIO_GetBusOSDevice, failed registering driver: %s, err: %d\n",
            pDriver->name, err));
        return OSErrorToSDIOError(err);
    }

    pDevice = pnp_alloc_dev(&sdio_protocol, slotNumber - 1, pFdid[0].id);
    if (!pDevice)
        return -EINVAL;

    if (pDma != NULL) {
        pDevice->dev.dma_mask = &pDma->Mask;
        pDevice->dev.coherent_dma_mask = pDma->Mask;
    }
    DBG_PRINT(SDDBG_ERROR,
            ("SDIO BusDriver - SDIO_GetBusOSDevice, registering driver: %s DMAmask: 0x%x\n",
            pDriver->name, (UINT)*pDevice->dev.dma_mask));

    pDevice->protocol = &sdio_protocol;
    pDevice->capabilities = PNP_REMOVABLE | PNP_DISABLE;
    pDevice->active = 1;


    /* get a unique device number */
    spin_lock(&InUseDevicesLock);
    pDevice->number = FirstClearBit(&InUseDevices);
    SetBit(&InUseDevices, pDevice->number);
    spin_unlock(&InUseDevicesLock);

    err = pnp_add_device(pDevice);
    if (err < 0) {
        DBG_PRINT(SDDBG_ERROR, ("SDIO BusDriver - SDIO_GetBusOSDevice failed pnp_device_add: %d\n",
                               err));
        pnp_unregister_driver(pDriver);
    }
    /* replace the buggy pnp's release */
    pDevice->dev.release = release;
    *outDevice = pDevice;
    return OSErrorToSDIOError(err);
}

/**+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  @function: Return OS device from bus driver.

  @function name: SDIO_BusRemoveOSDevice
  @category: HD_Reference

  @input: pDriver - setup PNP driver object
  @input: pDevice - setup PNP device object

  @return: none


  @example: returning device objects:
        SDIO_BusRemoveOSDevice(&HcdContext.Driver, &HcdContext.Device);


  @see also: SDIO_BusAddOSDevice

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void SDIO_BusRemoveOSDevice(POS_PNPDRIVER pDriver, POS_PNPDEVICE pDevice)
{
    DBG_PRINT(SDDBG_ERROR,
            ("SDIO BusDriver - SDIO_PutBusOSDevice, unregistering driver: %s\n",
            pDriver->name));

    pnp_remove_card_device(pDevice);
    if (pDevice->id != NULL) {
        KernelFree(pDevice->id);
        pDevice->id = NULL;
    }

    spin_lock(&InUseDevicesLock);
    ClearBit(&InUseDevices, pDevice->number);
    spin_unlock(&InUseDevicesLock);

    pnp_unregister_driver(pDriver);
    if (pDriver->id_table != NULL) {
        KernelFree((void *)pDriver->id_table);
        pDriver->id_table = NULL;
    }

}


/*
 * module init
*/
static int __init sdio_busdriver_init(void) {
    SDIO_STATUS status;
    int error;
    REL_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: loaded\n"));
    if (!SDIO_SUCCESS((status = _SDIO_BusDriverInitialize()))) {
        return SDIOErrorToOSError(status);
    }
    /* register the sdio bus */
    error = pnp_register_protocol(&sdio_protocol);
    if (error < 0) {
        REL_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: failed to register bus device, %d\n", error));
        _SDIO_BusDriverCleanup();
        return error;
    }
    return 0;
}

/*
 * module cleanup
*/
static void __exit sdio_busdriver_cleanup(void) {
    REL_PRINT(SDDBG_TRACE, ("SDIO unloaded\n"));
    _SDIO_BusDriverCleanup();
    pnp_unregister_protocol(&sdio_protocol);
DBG_PRINT(SDDBG_TRACE,
            ("SDIO BusDriver - unloaded 1\n"));
}
EXPORT_SYMBOL(SDIO_BusAddOSDevice);
EXPORT_SYMBOL(SDIO_BusRemoveOSDevice);

#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
    /* 2.4 */
static int RegisterDriver(PSDFUNCTION pFunction)
{
    return 0;
}

static int UnregisterDriver(PSDFUNCTION pFunction)
{
    DBG_PRINT(SDDBG_TRACE,
            ("+-SDIO BusDriver - UnregisterDriver, driver: \n"));
   return 0;
}

/*
 * OS_InitializeDevice - initialize device that will be registered
*/
SDIO_STATUS OS_InitializeDevice(PSDDEVICE pDevice, PSDFUNCTION pFunction)
{
    return SDIO_STATUS_SUCCESS;
}

/*
 * OS_AddDevice - must be pre-initialized with OS_InitializeDevice
*/
SDIO_STATUS OS_AddDevice(PSDDEVICE pDevice, PSDFUNCTION pFunction)
{
    DBG_PRINT(SDDBG_TRACE, ("SDIO BusDriver - OS_AddDevice adding function: %s\n",
                               pFunction->pName));
    return SDIO_STATUS_SUCCESS;

}

/*
 * OS_RemoveDevice - unregister device with driver and bus
*/
void OS_RemoveDevice(PSDDEVICE pDevice)
{
    DBG_PRINT(SDDBG_TRACE, ("SDIO BusDriver - OS_RemoveDevice \n"));
}

/*
 * module init
*/
static int __init sdio_busdriver_init(void) {
    SDIO_STATUS status;
    REL_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: loaded\n"));
    if (!SDIO_SUCCESS((status = _SDIO_BusDriverInitialize()))) {
        return SDIOErrorToOSError(status);
    }
    return 0;
}

/*
 * module cleanup
*/
static void __exit sdio_busdriver_cleanup(void) {
    REL_PRINT(SDDBG_TRACE, ("SDIO unloaded\n"));
    _SDIO_BusDriverCleanup();
}
#else  ////KERNEL_VERSION
#error "unsupported kernel version: "UTS_RELEASE
#endif //KERNEL_VERSION

MODULE_LICENSE("GPL and additional rights");
MODULE_DESCRIPTION(DESCRIPTION);
MODULE_AUTHOR(AUTHOR);

module_init(sdio_busdriver_init);
module_exit(sdio_busdriver_cleanup);
EXPORT_SYMBOL(SDIO_RegisterHostController);
EXPORT_SYMBOL(SDIO_UnregisterHostController);
EXPORT_SYMBOL(SDIO_HandleHcdEvent);
EXPORT_SYMBOL(SDIO_CheckResponse);
EXPORT_SYMBOL(SDIO_RegisterFunction);
EXPORT_SYMBOL(SDIO_UnregisterFunction);