Le 06/03/2013 22:05, Gilles Chanteperdrix a écrit :
On 03/06/2013 04:28 PM, Jan Kiszka wrote:

Also here: Do not misuse write(), define an IOCTL that contains
something like "MMAP" and another one with MUNMAP in its name - that's
the purpose of this service, no?

A lot of drivers probably have to reinvent a way to pass all the
parameters for an mmap, why not adding an "mmap" method to RTDM drivers?


Hello,

I  re factored my code with all your remarks (I think and I hope).

Could you say me if it's correct now ?

Jan, you're right, It's possible to use Xenomai with UIO driver, but I would like to use more real time possibilities of Xenomai in future (like IRQ).
My first goal is to have a code compliant.
I added IRQ, and my next step will be to understand why the kernel crash after IRQ registration.

Thank you for your help,

Jerome



-------------- next part --------------
/*
* Copyright (C) 2013 Hilscher France (JP) <www.hilscher.fr>
*
* Xenomai is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Xenomai is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Xenomai; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <linux/device.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/io.h>
#include <linux/mman.h>

#include <asm-generic/xenomai/pci_ids.h>

#include <rtdm/rtdm_driver.h>

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("RTDM board driver for CifX cards");
MODULE_AUTHOR("Hilscher France (JP) <www.hilscher.fr>");

#define RTCIFX_GET_MEM_INFO                             0x00
#define RTCIFX_MMAP                                     0x01
#define RTCIFX_MUNMAP                                   0x02
#define RTCIFX_REQUEST_IRQ                              0x03
#define RTCIFX_FREE_IRQ                                 0x04
#define RTCIFX_ENABLE_IRQ                               0x05
#define RTCIFX_DISABLE_IRQ                              0x06

#define DPM_HOST_INT_EN0                0xfff0
#define DPM_HOST_INT_STAT0              0xffe0
#define PLX_GPIO_OFFSET                 0x15
#define PLX_TIMING_OFFSET               0x0a

#define DPM_HOST_INT_MASK               0xe600ffff
#define DPM_HOST_INT_GLOBAL_EN          0x80000000
#define PLX_GPIO_DATA0_MASK             0x00000004
#define PLX_GPIO_DATA1_MASK             0x00000020

#define NX_PCA_PCI_8_BIT_DPM_MODE       0x5431F962
#define NX_PCA_PCI_16_BIT_DPM_MODE      0x4073F8E2
#define NX_PCA_PCI_32_BIT_DPM_MODE      0x40824122

/* Number of bar */
/* points to the DPM -> netX, netPLC, netJACK */
#define DPM_BAR                         0
/* points to the optional extended memory     */
#define EXT_MEM_BAR                     1
/* points to the DPM -> netXPLX               */
#define PLX_DPM_BAR                     2
/* timing config register                     */
#define PXA_PLX_BAR                     0

/* Index of io_info structure's memory array */
/* first mapping describes DPM              */
#define DPM_INDEX                       0
/* second mapping describes extended memory */
#define EXT_MEM_INDEX                   1

#define MAX_MAPS                        2

#define MEM_PHYS                        1

#define DRIVER_NAME                     "rtdm_cifx"
#define PERIPHERAL_NAME                 "cifx"
#define PROVIDER_NAME                   "Hilscher"

/* name of a NXSB-PCA or NXPCA-PCI card */
#define CIFX_RTDM_PLX_CARD_NAME         "netx_plx"
/* name of a cifX PCI card              */
#define CIFX_RTDM_CARD_NAME             "netx"
/* name of a netPLC PCI card            */
#define CIFX_RTDM_NETPLC_CARD_NAME      "netplc"
/* name of a netJACK PCI card           */
#define CIFX_RTDM_NETJACK_CARD_NAME     "netjack"

struct io_mem {
        uint32_t addr;
        uint32_t size;
        int32_t memtype;
        void __iomem *internal_addr;
};

struct io_info {
        struct io_mem mem[MAX_MAPS];
        int32_t irq;
        bool irq_enable;
        bool irq_registered;
        rtdm_irq_t irq_handle;
        struct ext_io_info *priv;
};

struct ext_io_info {
        uint32_t __iomem *plx;
        uint8_t dpm_mode;
        uint32_t plx_timing;
};

struct io_map_mem {
        uint32_t phys_addr;
        uint32_t virt_addr;
        uint32_t length;
};

static struct io_info *cifx_get_device_data(struct rtdm_device *info);
static struct ext_io_info *cifx_get_private(struct rtdm_device *info);

static int cifx_set_plx_timing(struct ext_io_info *ext_info);
static int cifx_get_plx_timing(struct ext_io_info *ext_info);
static int cifx_get_dpm_mode(struct ext_io_info *ext_info);

static int cifx_pci_open(struct rtdm_dev_context *context,
                         rtdm_user_info_t *user_info, int oflags);
static int cifx_pci_close(struct rtdm_dev_context *context,
                          rtdm_user_info_t *user_info);
static int cifx_pci_ioctl(struct rtdm_dev_context *context,
                          rtdm_user_info_t *user_info,
                          unsigned int request, void *arg);

static int cifx_pci_probe(struct pci_dev *dev, const struct pci_device_id *id);
static void cifx_pci_remove(struct pci_dev *dev);

static inline int cifx_handler(rtdm_irq_t *irq);

/* Number or cifx found and open */
static int32_t cifx_num;

/* RTDM Device information structure */
static const struct rtdm_device __initdata cifx_device_tmpl = {
        .struct_version = RTDM_DEVICE_STRUCT_VER,

        .device_flags = RTDM_NAMED_DEVICE,
        .context_size = 0,
        .device_name = "",

        .open_nrt = cifx_pci_open,

        .ops = {
                .close_nrt = cifx_pci_close,

                .read_nrt = NULL,
                .write_nrt = NULL,

                .ioctl_rt = NULL,
                .ioctl_nrt = cifx_pci_ioctl,

                .read_rt = NULL,
                .write_rt = NULL,
                },

        .device_class = RTDM_CLASS_EXPERIMENTAL,
        .device_sub_class = RTDM_SUBCLASS_GENERIC,
        .profile_version = 1,
        .driver_name = DRIVER_NAME,
        .driver_version = RTDM_DRIVER_VER(1, 0, 0),
        .provider_name = PROVIDER_NAME,
};

/* Device table */
static DEFINE_PCI_DEVICE_TABLE(cifx_pci_tbl) = {
        {
        PCI_VENDOR_ID_HILSCHER, PCI_DEVICE_ID_HILSCHER_NETX, 0, 0}, {
        PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030,
                    PCI_VENDOR_ID_PLX, PCI_SUBDEVICE_ID_NXSB_PCA}, {
        PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030,
                    PCI_VENDOR_ID_PLX, PCI_SUBDEVICE_ID_NXPCA}, {
        PCI_VENDOR_ID_HILSCHER, PCI_DEVICE_ID_HILSCHER_NETPLC,
                    PCI_VENDOR_ID_HILSCHER, PCI_SUBDEVICE_ID_NETPLC_RAM}, {
        PCI_VENDOR_ID_HILSCHER, PCI_DEVICE_ID_HILSCHER_NETPLC,
                    PCI_VENDOR_ID_HILSCHER, PCI_SUBDEVICE_ID_NETPLC_FLASH}, {
        PCI_VENDOR_ID_HILSCHER, PCI_DEVICE_ID_HILSCHER_NETJACK,
                    PCI_VENDOR_ID_HILSCHER, PCI_SUBDEVICE_ID_NETJACK_RAM}, {
        PCI_VENDOR_ID_HILSCHER, PCI_DEVICE_ID_HILSCHER_NETJACK,
                    PCI_VENDOR_ID_HILSCHER, PCI_SUBDEVICE_ID_NETJACK_FLASH}, {
        0,}
};

/* cifX Driver */
static struct pci_driver cifx_pci_driver = {
        .name = "cifx",
        .id_table = cifx_pci_tbl,
        .probe = cifx_pci_probe,
        .remove = cifx_pci_remove,
};

/*
*       cifx_rtdm_device_to_device_data
*       Get device_data structure
*/
static struct io_info *cifx_get_device_data(struct rtdm_device *info)
{
        return info->device_data;
}

/*
*       cifx_rtdm_device_to_private
*       Get ext_io_info structure
*/
static struct ext_io_info *cifx_get_private(struct rtdm_device *info)
{
        return ((struct io_info *)info->device_data)->priv;
}

/*
* cifx_handler
* cifx interrupt handler
*/
static inline int cifx_handler(rtdm_irq_t *irq)
{
        struct rtdm_device *info = rtdm_irq_get_arg(irq, void);
        struct io_info *device_data = info->device_data;

        /* Test if request is for this driver */
        if ((device_data->priv != NULL)
            || (device_data->irq_registered == 0)
            || (device_data->irq_enable == 0)) {
                /* This is a PLX device and cannot produce an IRQ,
                   IRQ not registred or not enable (cannot produce an IRQ) */
                return RTDM_IRQ_NONE;
        } else {
                void __iomem *int_enable_reg =
                    device_data->mem[DPM_INDEX].internal_addr +
                    DPM_HOST_INT_EN0;
                void __iomem *int_status_reg =
                    device_data->mem[DPM_INDEX].internal_addr +
                    DPM_HOST_INT_STAT0;

                /* Is one of our interrupts enabled and active ? */
                if (!(ioread32(int_enable_reg) & ioread32(int_status_reg)
                      & DPM_HOST_INT_MASK))
                        return RTDM_IRQ_NONE;

                /* Disable interrupt */
                iowrite32(ioread32(int_enable_reg)
                          & ~DPM_HOST_INT_GLOBAL_EN, int_enable_reg);

                return RTDM_IRQ_HANDLED;
        }
}

/*
*       cifx_set_plx_timing
*       Set plx timing
*/
static int cifx_set_plx_timing(struct ext_io_info *ext_info)
{
        uint32_t __iomem *plx_timing;

        if (ext_info == NULL)
                return -ENODEV;

        plx_timing = ext_info->plx + PLX_TIMING_OFFSET;
        *plx_timing = ext_info->plx_timing;

        return 0;
}

/*
* cifx_get_plx_timing
* Get plx timing
*/
static int cifx_get_plx_timing(struct ext_io_info *ext_info)
{
        if (ext_info == NULL)
                return -ENODEV;

        switch (ext_info->dpm_mode) {
        case 8:
                ext_info->plx_timing = NX_PCA_PCI_8_BIT_DPM_MODE;
                break;
        case 16:
                ext_info->plx_timing = NX_PCA_PCI_16_BIT_DPM_MODE;
                break;
        case 32:
                ext_info->plx_timing = NX_PCA_PCI_32_BIT_DPM_MODE;
                break;
        default:
                return -EINVAL;
        }

        return 0;
}

/*
* cifx_get_dpm_mode
* Get dpm mode
*/
static int cifx_get_dpm_mode(struct ext_io_info *ext_info)
{
        uint32_t __iomem *plx_gpio;

        if (ext_info == NULL)
                return -ENODEV;

        plx_gpio = ext_info->plx + PLX_GPIO_OFFSET;

        if ((*plx_gpio & PLX_GPIO_DATA0_MASK)
            && ~(*plx_gpio & PLX_GPIO_DATA1_MASK))
                ext_info->dpm_mode = 8;
        else if (~(*plx_gpio & PLX_GPIO_DATA0_MASK)
                 && (*plx_gpio & PLX_GPIO_DATA1_MASK))
                ext_info->dpm_mode = 32;
        else if (~(*plx_gpio & PLX_GPIO_DATA0_MASK)
                 && ~(*plx_gpio & PLX_GPIO_DATA1_MASK))
                ext_info->dpm_mode = 16;
        else
                return -EINVAL;

        return 0;
}

/*
* cifx_pci_open
* Open cifx pci driver
*/
static int cifx_pci_open(struct rtdm_dev_context *context,
                         rtdm_user_info_t *user_info, int oflags)
{
        return 0;
}

/*
* cifx_pci_close
* Close cifx pci driver
*/
static int cifx_pci_close(struct rtdm_dev_context *context,
                          rtdm_user_info_t *user_info)
{
        return 0;
}

/*
* cifx_pci_ioctl
* ioctl
*/
static int cifx_pci_ioctl(struct rtdm_dev_context *context,
                          rtdm_user_info_t *user_info,
                          unsigned int request, void *arg)
{
        int ret = 0;
        struct rtdm_device *info = (struct rtdm_device *)context->device;
        struct io_info *device_data = cifx_get_device_data(info);
        struct io_map_mem map_mem;

        switch (request) {
        case RTCIFX_GET_MEM_INFO:
                if (arg == NULL)
                        return -EINVAL;

                ret = rtdm_safe_copy_to_user(user_info,
                                             arg,
                                             device_data->mem,
                                             (2 * sizeof(uint32_t)));
                break;

        case RTCIFX_MMAP:
                if (arg == NULL)
                        return -EINVAL;

                ret = rtdm_safe_copy_from_user(user_info,
                                               &map_mem, arg, sizeof(map_mem));
                if (ret != 0)
                        break;

                /* Map physical on virtual memory */
                ret = rtdm_iomap_to_user(user_info,
                                         map_mem.phys_addr,
                                         map_mem.length,
                                         (PROT_READ | PROT_WRITE),
                                         (void **)&map_mem.virt_addr,
                                        NULL,
                                        NULL);
                if (ret != 0)
                        break;

                ret = rtdm_safe_copy_to_user(user_info,
                                                arg,
                                                &map_mem,
                                                sizeof(map_mem));
                break;

        case RTCIFX_MUNMAP:
                if (arg == NULL)
                        return -EINVAL;

                ret = rtdm_safe_copy_from_user(user_info,
                                               &map_mem, arg, sizeof(map_mem));
                if (ret != 0)
                        break;

                /* Unap virtual memory */
                ret = rtdm_munmap(user_info,
                                  (void *)map_mem.virt_addr, map_mem.length);
                break;

        case RTCIFX_REQUEST_IRQ:
                if (device_data->irq_registered == 0) {
                        ret = rtdm_irq_request(&device_data->irq_handle,
                                               device_data->irq,
                                               cifx_handler,
                                               RTDM_IRQTYPE_SHARED,
                                               info->device_name, (void *)info);

                        if (ret == 0)
                                device_data->irq_registered = 1;
                }
                break;

        case RTCIFX_FREE_IRQ:
                if (device_data->irq_registered == 1) {
                        ret = rtdm_irq_free(&device_data->irq_handle);

                        if (ret == 0)
                                device_data->irq_registered = 0;
                }
                break;

        case RTCIFX_ENABLE_IRQ:
                if (device_data->irq_registered == 1) {
                        ret = rtdm_irq_enable(&device_data->irq_handle);

                        if (ret == 0)
                                device_data->irq_enable = 1;
                }
                break;

        case RTCIFX_DISABLE_IRQ:
                if (device_data->irq_registered == 1) {
                        ret = rtdm_irq_disable(&device_data->irq_handle);

                        if (ret == 0)
                                device_data->irq_enable = 0;
                }
                break;

        default:
                ret = -EINVAL;
                break;
        }

        return ret;
}

static int cifx_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
        struct rtdm_device *info = NULL;
        struct io_info *device_data = NULL;
        int32_t bar;
        int32_t ret = -ENODEV;

        /* Allocate device driver structure */
        info = kmalloc(sizeof(*info), GFP_KERNEL);

        if (info == NULL)
                return -ENOMEM;

        ret = pci_enable_device(dev);
        if (ret != 0)
                goto out_free;

        ret = pci_request_regions(dev, DRIVER_NAME);
        if (ret != 0)
                goto out_disable;

        /* Initialize structure with default values */
        memcpy(info, &cifx_device_tmpl, sizeof(*info));

        info->device_id = id->device;
        snprintf(info->device_name, RTDM_MAX_DEVNAME_LEN, "cifx%i", cifx_num);
        info->proc_name = info->device_name;

        switch (id->device) {
        case PCI_DEVICE_ID_HILSCHER_NETX:
                bar = DPM_BAR;
                info->peripheral_name = CIFX_RTDM_CARD_NAME;
                break;
        case PCI_DEVICE_ID_HILSCHER_NETPLC:
                bar = DPM_BAR;
                info->peripheral_name = CIFX_RTDM_NETPLC_CARD_NAME;
                break;
        case PCI_DEVICE_ID_HILSCHER_NETJACK:
                bar = DPM_BAR;
                info->peripheral_name = CIFX_RTDM_NETJACK_CARD_NAME;
                break;
        default:
                bar = PLX_DPM_BAR;
                info->peripheral_name = CIFX_RTDM_PLX_CARD_NAME;
                break;
        }

        info->device_data = NULL;

        /* Allocate specific data strcuture for device */
        device_data = kmalloc(sizeof(*device_data), GFP_KERNEL);

        if (device_data == NULL) {
                ret = -ENOMEM;
                goto out_release;
        }

        memset(device_data, 0, sizeof(*device_data));

        /* BAR 0 or 2 points to the card's dual port memory */
        device_data->mem[DPM_INDEX].addr = pci_resource_start(dev, bar);

        if (device_data->mem[DPM_INDEX].addr == 0)
                goto out_release;

        device_data->mem[DPM_INDEX].internal_addr =
            ioremap_nocache(pci_resource_start(dev, bar),
                            pci_resource_len(dev, bar));

        if (device_data->mem[DPM_INDEX].internal_addr == 0)
                goto out_release;

        dev_info(&dev->dev, "DPM at %08X\n", device_data->mem[DPM_INDEX].addr);
        device_data->mem[DPM_INDEX].size = pci_resource_len(dev, bar);
        device_data->mem[DPM_INDEX].memtype = MEM_PHYS;

        /* map extended mem (BAR 1 points to the extended memory) */
        device_data->mem[EXT_MEM_INDEX].addr =
            pci_resource_start(dev, EXT_MEM_BAR);

        /* extended memory is optional, so don't care if it is not present */
        if (device_data->mem[EXT_MEM_INDEX].addr != 0) {
                device_data->mem[EXT_MEM_INDEX].internal_addr =
                    ioremap_nocache(pci_resource_start(dev, EXT_MEM_BAR),
                                    pci_resource_len(dev, EXT_MEM_BAR));

                if (device_data->mem[EXT_MEM_INDEX].internal_addr == 0)
                        goto out_unmap;

                dev_info(&dev->dev,
                         "extended memory at %08X\n",
                         device_data->mem[EXT_MEM_INDEX].addr);
                device_data->mem[EXT_MEM_INDEX].size =
                    pci_resource_len(dev, EXT_MEM_BAR);
                device_data->mem[EXT_MEM_INDEX].memtype = MEM_PHYS;
        }

        if ((id->device == PCI_DEVICE_ID_HILSCHER_NETX)
            || (id->device == PCI_DEVICE_ID_HILSCHER_NETPLC)
            || (id->device == PCI_DEVICE_ID_HILSCHER_NETJACK)) {
                /* make sure all interrupts are disabled */
                iowrite32(0, device_data->mem[DPM_INDEX].internal_addr
                          + DPM_HOST_INT_EN0);
                device_data->priv = NULL;
        } else if (id->subdevice == PCI_SUBDEVICE_ID_NXPCA) {
                /* map PLX registers */
                struct ext_io_info *ext_info;

                ext_info = kmalloc(sizeof(*ext_info), GFP_KERNEL);

                if (ext_info == NULL) {
                        ret = -ENOMEM;
                        goto out_unmap;
                }

                device_data->priv = ext_info;

                /* set PXA PLX Timings */
                ext_info->plx =
                    ioremap_nocache(pci_resource_start(dev, PXA_PLX_BAR),
                                    pci_resource_len(dev, PXA_PLX_BAR));

                if (ext_info->plx == NULL)
                        goto out_unmap;
                if (cifx_get_dpm_mode(ext_info))
                        goto out_unmap_plx;
                if (cifx_get_plx_timing(ext_info))
                        goto out_unmap_plx;
                if (cifx_set_plx_timing(ext_info))
                        goto out_unmap_plx;
        } else {
                struct ext_io_info *ext_info;

                ext_info = kmalloc(sizeof(*ext_info), GFP_KERNEL);

                if (ext_info == NULL) {
                        ret = -ENOMEM;
                        goto out_free_pxa;
                }

                ext_info->plx = NULL;
                ext_info->plx_timing = 0;
                ext_info->dpm_mode = 0;
                device_data->priv = ext_info;
        }

        /* Initialize irq data */
        device_data->irq = dev->irq;
        device_data->irq_enable = 0;
        device_data->irq_registered = 0;

        info->device_data = device_data;

        /* Register RTDM device driver */
        ret = rtdm_dev_register(info);
        if (ret != 0) {
                if (id->subdevice != PCI_SUBDEVICE_ID_NXPCA)
                        goto out_unmap;
                else
                        goto out_unmap_plx;
        }

        pci_set_drvdata(dev, info);

        if (id->device == PCI_DEVICE_ID_HILSCHER_NETX)
                dev_info(&dev->dev, "registered CifX card\n");
        else if (id->device == PCI_DEVICE_ID_HILSCHER_NETPLC)
                dev_info(&dev->dev, "registered netPLC card\n");
        else if (id->device == PCI_DEVICE_ID_HILSCHER_NETJACK)
                dev_info(&dev->dev, "registered netJACK card\n");
        else if (id->subdevice == PCI_SUBDEVICE_ID_NXSB_PCA)
                dev_info(&dev->dev, "registered NXSB-PCA adapter card\n");
        else {
                struct ext_io_info *ext_info = device_data->priv;
                dev_info(&dev->dev,
                         "registered NXPCA-PCI adapter card in %d bit mode\n",
                         ((struct ext_io_info *)ext_info)->dpm_mode);
        }

        cifx_num++;

        return 0;

out_unmap_plx:
        iounmap((device_data->priv)->plx);

out_free_pxa:
        kfree(device_data->priv);

out_unmap:
        iounmap(device_data->mem[DPM_INDEX].internal_addr);
        if (device_data->mem[EXT_MEM_INDEX].internal_addr != 0)
                iounmap(device_data->mem[EXT_MEM_INDEX].internal_addr);

out_release:
        pci_release_regions(dev);

out_disable:
        pci_disable_device(dev);

out_free:
        if (info->device_data != NULL)
                kfree(info->device_data);

        kfree(info);

        return ret;
}

static void cifx_pci_remove(struct pci_dev *dev)
{
        struct rtdm_device *info = pci_get_drvdata(dev);
        struct io_info *device_data = cifx_get_device_data(info);
        struct ext_io_info *ext_info = cifx_get_private(info);

        if (cifx_num > 0) {
                if (info->device_data == NULL)
                        return;

                if (ext_info != NULL) {
                        /* Disable all interrupts */
                        iowrite32(0, device_data->mem[DPM_INDEX].internal_addr
                                  + DPM_HOST_INT_EN0);

                        if (ext_info->plx != NULL)
                                iounmap((void *)ext_info->plx);

                        kfree((void *)ext_info);
                }

                /* Unregister device driver */
                rtdm_dev_unregister(info, 1000);

                pci_release_regions(dev);
                pci_disable_device(dev);
                pci_set_drvdata(dev, NULL);

                /* Unmap memory */
                iounmap(device_data->mem[DPM_INDEX].internal_addr);
                if (device_data->mem[EXT_MEM_INDEX].internal_addr != 0)
                        iounmap(device_data->mem[EXT_MEM_INDEX].internal_addr);

                /* Release structure memory allocation */
                kfree(info->device_data);
                kfree(info);

                cifx_num--;
        }
}

static int __init cifx_pci_init(void)
{
        return pci_register_driver(&cifx_pci_driver);
}

static void __exit cifx_pci_exit(void)
{
        pci_unregister_driver(&cifx_pci_driver);
}

module_init(cifx_pci_init);
module_exit(cifx_pci_exit);
_______________________________________________
Xenomai mailing list
[email protected]
http://www.xenomai.org/mailman/listinfo/xenomai

Reply via email to