Hi,

(removed alsa-users from Cc since focused on the development only.)

At Mon, 02 Dec 2002 17:11:20 +0800,
Alamy Liu wrote:
> 
> I would like to take it.  May I ?
> But I need guidance.

the geode driver seems having the followng additional files to the
0.5.10b driver tree:

- include/geode.h
- cards/card-geode.c
- lowlevel/pci/geode.c
- lowlevel/pci/duraudio.c
- lowlevel/pci/5530_def.h
- lowlevel/pci/5530_glb.h
- lowlevel/pci/os_inc.h

on the alsa 0.9.0 tree, you should create a driver code either into a
single file

- pci/geode.c

or files under sub-directory

- pci/geode/*.[ch]

please note that even the card-specific header files should go into
this sub-directory unless they need to be exported to user-space.


after i took a further look at the codes, i found that there are many
unnecessary parts in the files above.
especially, most of duraudio.c shouldn't be used.  it's evil.
also, the macros in os_inc.h should be expanded, i.e. the standard 
functions instead of redefined macro should be used.
because of these things, the porting may take longer than i
expected...


anyway, let me explain the first step.

the basic structure of the code would be as same as other pci
drivers.  please check some driver sources.  for example, es1938.c or 
maestro3.c are good example.  both of them have the sources on 0.5.x
tree, so you can compare the differences.

you'll have *_init() function just calling pci_module_init() to
register the pci_driver table which contains name, id_table, probe and
remove pointers.
also, *_exit() function to call pci_unregister_driver().

the real init part is done in *_probe() callback.
there, check and increment the device index at first.
then create a card: snd_card_new().

the chip-specific data are stored in a chip record.
this can be allocated in two ways.

1) allocating via snd_card_new().
   you can pass the extra-data-length to the 4th argument of
   snd_card_new(), i.e.
        card = snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof(geode_t))
   whether geode_t is the chip record.
   in return, the allocated record can be acceseed as
        geode_t *chip = (geode_t *)card->private_data;

   with this method, you don't have to allocate twice.  but you cannot
   use "magic-cast" for this record pointer, instead.  (see below)

2) allocating an extra device.
   after allocating a card instance via snd_card_new() (with NULL on
   the 4th arg), call snd_magic_kcalloc().

        card = snd_card_new(index[dev], id[dev], THIS_MODULE, NULL);
        ....
        chip = snd_magic_kcalloc(geode_t, 0, GFP_KERNEL);
        chip->card = card;
        ...
    you need to define a magic-value for geode_t (in include/sndmagic.h)
        #define snd_usb_midi_in_endpoint_t_magic        0xa15a4501
    the value is arbitrary but should be unique.

    initialize the fields, and register this chip record as a lowlevel
    device with a specifed ops,

        static snd_device_ops_t ops = {
                .dev_free =     snd_geode_dev_free,
        };
        snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);

    snd_geode_dev_free() is the device-destructor, which will call

        static int snd_geode_dev_free(snd_device_t *device)
        {
                geode_t *chip = snd_magic_cast(geode_t, device->device_data, return 
-ENXIO);
                return snd_geode_free(chip);
        }

   where snd_geode_free() is the real destructor.


what is the advantage of the second method?
as mentioned above, the second method allows a "magic-cast" for
geode_t.  if you have a void pointer (such like pcm->private_data),
the pointer type is unknown at the compile time, and you cannot know
even if a wrong pointer is passed.  the magic-cast checks the pointer
type at the runtime, so it's good for debugging.

for casting a pointer, do like this:

        geode_t *chip = snd_magic_cast(geode_t, source_pointer, action);

source_pointer is the pointer to be casted (e.g. pcm->private_data)
action is the action to do if the cast fails (e.g. return -EINVAL).

now you can check and allocate resources.
don't forget to call pci_enable_device() before allocating resources.
also you have to call pci_set_dma_mask() with 28bit mask
(0x0fffffff) for geode chips.

on 0.9.0, you don't need allocate DMA for PCI like 0.5.x.
allocation of ports and irqs are done via standard kernel functions.
unlike 0.5.x., there are no helpers for that.
and these resources must be released in the destructor function (see
below).

when the resources are allocated, then we can create pcm and mixer
stuffs.  but let's take them later on and write a destructor at
first.

the role of destructor is simple: disable the hardware (if already
activated) and release the resources.
for releasing the resource, "check-and-release" method is a safer way.
i.e.
        
        static int snd_geode_free(geode_t *chip)
        {
                if (chip->res_port) {
                        release_resource(chip->res_port);
                        kfree_nocheck(chip->res_port);
                }
                if (chip->irq >= 0)
                        free_irq(chip->irq, (void *)chip);
                snd_magic_kfree(chip);
                return 0;
        }

as you can see, the i/o resources are also to be freed via
kfree_nocheck() after release_resource() is called.


now, let's finish the pci_driver data.
write a pci_device_id table for this chipset,

        static struct pci_device_id snd_geode_ids[] __devinitdata = {
                { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_UNICORN, PCI_ANY_ID, PCI_ANY_ID, 
0, 0, 0, },
                { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CENTAURUS, PCI_ANY_ID, 
PCI_ANY_ID, 0, 0, 0, },
                { PCI_VENDOR_ID_NSC, PCI_DEVICE_ID_CENTAURUS, PCI_ANY_ID, PCI_ANY_ID, 
0, 0, 0, },
                { PCI_VENDOR_ID_NSC, PCI_DEVICE_ID_SCORPIUS, PCI_ANY_ID, PCI_ANY_ID, 
0, 0, 0, },
                { 0, }
        };
        MODULE_DEVICE_TABLE(pci, snd_geode_ids);

and

        static struct pci_driver driver = {
                .name = "NSC/Cyrix Geode",
                .id_table = snd_geode_ids,
                .probe = snd_geode_probe,
                .remove = __devexit_p(snd_geode_remove),
        };


        static int __init alsa_card_geode_init(void)
        {
                int err;

                if ((err = pci_module_init(&driver)) < 0) {
#ifdef MODULE
                        printk(KERN_ERR "NSC/Cyrix Geode soundcard not found or device 
busy\n");
#endif
                        return err;
                }
                return 0;
        }

        static void __exit alsa_card_geode_exit(void)
        {
                pci_unregister_driver(&driver);
        }

        module_init(alsa_card_geode_init)
        module_exit(alsa_card_geode_exit)


you can at least comple and build the module now (although not
functional yet :)
let's proceed to the pcm and ac97 part at next...


ciao,

Takashi


-------------------------------------------------------
This sf.net email is sponsored by:ThinkGeek
Welcome to geek heaven.
http://thinkgeek.com/sf
_______________________________________________
Alsa-devel mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/alsa-devel

Reply via email to