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