I am currently working on a PCI driver for the Xilinx Kintex 7 board using the Xilinx PCI IP core (AXI Memory Mapped to PCIe).
One problem is, that the interrupt handler stops working when I reload the kernel module. In more detail:
Fresh boot of my machine
Load the kernel module and monitor the kernel messages with dmesg
/proc/interrupts shows the expected interrupt ids
I trigger the HW interrupt and everything works as expected; I can see the interrupt handler working.
rmmod my_module
/proc/interrupts removed the interrupt ids as expected
insmod my_module and trigger interrupt
Now the interrupt handler is silent and /proc/interrupts does not increase the counter
I reboot my machine and everything works again. The fact that I do not have to restart the FPGA lets me assume that I do something wrong in the kernel module and its probably not an HW problem.
I've already used /sys/pci/devices/.../reset, /sys/bus/pci/devices/.../remove and /sys/bus/pci/rescan to try to reach a state which is equivalent to freshly booted machine. But nothing worked.
Relevant module code:
#define VENDOR_ID 0x10EE
#define DEVICE_ID 0x7024
static dev_t pci_dev_number;
static struct cdev * driver_object;
static struct class * pci_class;
static struct device * pci_prc;
static struct device * pci_irq_0;
static struct device * pci_irq_1;
static int msi_vec_num = 2; // Number of requested MSI interrupts
static int msi_0 = -1;
static int msi_1 = -1;
// Used for poll and select
static DECLARE_WAIT_QUEUE_HEAD(queue_vs0);
static DECLARE_WAIT_QUEUE_HEAD(queue_vs1);
static irqreturn_t pci_isr_0(int irq, void * dev_id) {
printk(KERN_NOTICE "codec IRQ: interrupt handler 0. IRQ: %d\n", irq);
wake_up_interruptible(&queue_vs0);
return IRQ_HANDLED;
}
static irqreturn_t pci_isr_1(int irq, void * dev_id) {
printk(KERN_NOTICE "codec IRQ: interrupt handler 1. IRQ: %d\n", irq);
wake_up_interruptible(&queue_vs1);
return IRQ_HANDLED;
}
static void* bars[PCIE_BARS] = {0};
static int device_init(struct pci_dev * pdev, const struct pci_device_id * id) {
int i = 0; // loop var
if (pci_enable_device(pdev))
return -EIO;
// Request memory regions for bar 0 to 2
for (i = 0; i < PCIE_BARS; i++) {
if (pci_request_region(pdev, i, "codec_pci") != 0) {
dev_err( & pdev - > dev, "Bar %d - I/O address conflict for device \"%s\"\n", i, pdev - > dev.kobj.name);
return -EIO;
}
}
// DEBUG: Check if we are in memory space (which we should) or io space
if ((pci_resource_flags(pdev, 0) & IORESOURCE_IO)) {
printk(KERN_NOTICE "codec INIT: in io space\n");
} else if ((pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
printk(KERN_NOTICE "codec INIT: in mem_space\n");
}
// This request enables MSI_enable in the hardware
msi_vec_num = pci_alloc_irq_vectors(pdev, 1, msi_vec_num, PCI_IRQ_MSI);
// msi_N will contain the IRQ number - see /proc/interrupts
msi_0 = pci_irq_vector(pdev, 0);
msi_1 = pci_irq_vector(pdev, 1);
printk(KERN_NOTICE "codec INIT: nvec: %d\n", msi_vec_num);
printk(KERN_NOTICE "codec INIT: msi_0: %d\n", msi_0);
printk(KERN_NOTICE "codec INIT: msi_1: %d\n", msi_1);
if (request_irq(msi_0, pci_isr_0, IRQF_SHARED, "codec_pci", pdev)) {
dev_err( & pdev - > dev, "codec INIT: IRQ MSI %d not free.\n", msi_0);
goto cleanup;
};
if (request_irq(msi_1, pci_isr_1, IRQF_SHARED, "codec_pci", pdev)) {
dev_err( & pdev - > dev, "codec INIT: IRQ MSI %d not free.\n", msi_1);
goto cleanup;
};
for (i = 0; i < PCIE_BARS; i++) {
// Last parameter is the address space/length of each bar. Defined in the PCIe core.
bars[i] = pci_iomap(pdev, i, pci_resource_len(pdev, i));
if (bars[i] == NULL) {
printk(KERN_ERR "codec INIT: bar %d allocation failed\n", i);
goto cleanup;
}
printk(KERN_NOTICE "codec INIT: bar %d pointer: %p\n", i, bars[i]);
}
printk(KERN_NOTICE "codec INIT: loaded\n");
return 0;
cleanup:
for (i = 0; i < PCIE_BARS; i++) {
if (bars[i] != NULL)
pci_iounmap(pdev, bars[i]);
pci_release_region(pdev, i);
}
return -EIO;
}
static void device_deinit(struct pci_dev * pdev) {
int i = 0; // loop var
if (msi_0 >= 0)
free_irq(msi_0, pdev);
if (msi_1 >= 0)
free_irq(msi_1, pdev);
pci_free_irq_vectors(pdev);
// release bar regions
for (i = 0; i < PCIE_BARS; i++)
pci_release_region(pdev, i);
for (i = 0; i < PCIE_BARS; i++) {
if (bars[i] != NULL)
pci_iounmap(pdev, bars[i]);
}
pci_disable_device(pdev);
}
// File operations not in this snipped
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = device_open,
.read = device_read,
.write = device_write,
.poll = device_poll
};
static struct pci_device_id pci_drv_tbl[] = {
{
VENDOR_ID,
DEVICE_ID,
PCI_ANY_ID,
PCI_ANY_ID,
0,
0,
0
},
{
0,
}
};
static struct pci_driver pci_drv = {
.name = "codec_pci",
.id_table = pci_drv_tbl,
.probe = device_init,
.remove = device_deinit
};
static int __init mod_init(void) {
int i = 0;
if (alloc_chrdev_region( & pci_dev_number, 0, MAX_DEVICES, "codec_pci") < 0)
return -EIO;
driver_object = cdev_alloc();
if (driver_object == NULL)
goto free_dev_number;
driver_object - > owner = THIS_MODULE;
driver_object - > ops = & fops;
if (cdev_add(driver_object, pci_dev_number, MAX_DEVICES))
goto free_cdev;
pci_class = class_create(THIS_MODULE, "codec_pci");
if (IS_ERR(pci_class)) {
pr_err("codec MOD_INIT: no udev support available\n");
goto free_cdev;
}
pci_prc = device_create(pci_class, NULL, MKDEV(MAJOR(pci_dev_number), MINOR(pci_dev_number) + 0), NULL, "%s", "codec_prc");
pci_irq_0 = device_create(pci_class, NULL, MKDEV(MAJOR(pci_dev_number), MINOR(pci_dev_number) + 1), NULL, "codec_irq_%d", 0);
pci_irq_1 = device_create(pci_class, NULL, MKDEV(MAJOR(pci_dev_number), MINOR(pci_dev_number) + 2), NULL, "codec_irq_%d", 1);
if (pci_register_driver( & pci_drv) < 0) {
for (i = 0; i < MAX_DEVICES; i++)
device_destroy(pci_class, MKDEV(pci_dev_number, i));
goto free_dev_number;
}
return 0;
free_cdev:
kobject_put( & driver_object - > kobj);
free_dev_number:
unregister_chrdev_region(pci_dev_number, MAX_DEVICES);
return -EIO;
}
static void __exit mod_exit(void) {
int i = 0;
pci_unregister_driver( & pci_drv);
device_unregister(pci_prc);
device_unregister(pci_irq_0);
device_unregister(pci_irq_1);
for (i = 0; i < MAX_DEVICES; i++) {
device_destroy(pci_class, MKDEV(pci_dev_number, i));
}
class_destroy(pci_class);
cdev_del(driver_object);
unregister_chrdev_region(pci_dev_number, MAX_DEVICES);
}
module_init(mod_init);
module_exit(mod_exit);
Error handling routines could be better, but they don't get triggered anyway.
I guess I found the cause of my problem. I took a look at the PCI configuration space while executing each of the steps of my original post. The configuration space when interrupts are working:
# lspci -xxx | grep Xilinx
23:00.0 Memory controller: Xilinx Corporation Device 7024
00: ee 10 24 70 *07* 04 10 00 00 00 80 05 10 00 00 00
. . .
And when it's broken:
# lspci -xxx | grep Xilinx
23:00.0 Memory controller: Xilinx Corporation Device 7024
00: ee 10 24 70 *03* 04 10 00 00 00 80 05 10 00 00 00
. . .
What I found is that the command register value changes after the kernel module reload (marked with *). When the interrupt works the command register value is 0x0407, after the module reload it is 0x0403. Why? I don't know. It is probably just the way the Xilinx AXI Memory Mapped to PCIe core is implemented.
Anyway, you can set the values of the PCI configuration space using setpci(8).
The wanted value of the command register is 0407 so you execute:
# setpci -d <vendor_id>:<device_id> command=0407
#read back to check if it worked
# sudo setpci -d <vendor_id>:<device_id> command
0407
Afterwards the interrupts are working again, and I do not need to reboot.
Within the kernel module you can e.g. use pci_write_config_byte(...) to set the command register (or any other) to the required value. The corresponding functions to access the configuration space can be found here: Linux Device Drivers - Accessing the Configuration Space
Related
The KVM irqfd ioctl starts the irqfd for a file descriptor.
It does this:
case KVM_IRQFD: {
struct kvm_irqfd data;
r = -EFAULT;
if (copy_from_user(&data, argp, sizeof(data)))
goto out;
r = kvm_irqfd(kvm, &data);
break;
}
where kvm_irqfd is here
and calls kvm_irqfd_assign which initiates a wakeup queue:
init_waitqueue_func_entry(&irqfd->wait, irqfd_wakeup);
That is, irqfd_wakeup does this:
if (flags & EPOLLIN) {
u64 cnt;
eventfd_ctx_do_read(irqfd->eventfd, &cnt);
idx = srcu_read_lock(&kvm->irq_srcu);
do {
seq = read_seqcount_begin(&irqfd->irq_entry_sc);
irq = irqfd->irq_entry;
} while (read_seqcount_retry(&irqfd->irq_entry_sc, seq));
/* An event has been signaled, inject an interrupt */
if (kvm_arch_set_irq_inatomic(&irq, kvm,
KVM_USERSPACE_IRQ_SOURCE_ID, 1,
false) == -EWOULDBLOCK)
schedule_work(&irqfd->inject);
srcu_read_unlock(&kvm->irq_srcu, idx);
ret = 1;
}
As you can see in schedule_work(&irqfd->inject), it schedules the inject function, which is here:
static void
irqfd_inject(struct work_struct *work)
{
struct kvm_kernel_irqfd *irqfd =
container_of(work, struct kvm_kernel_irqfd, inject);
struct kvm *kvm = irqfd->kvm;
if (!irqfd->resampler) {
kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID, irqfd->gsi, 1,
false);
kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID, irqfd->gsi, 0,
false);
} else
kvm_set_irq(kvm, KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID,
irqfd->gsi, 1, false);
}
It calls kvm_set_irq defined here which does this:
int kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level,
bool line_status)
{
struct kvm_kernel_irq_routing_entry irq_set[KVM_NR_IRQCHIPS];
int ret = -1, i, idx;
trace_kvm_set_irq(irq, level, irq_source_id);
/* Not possible to detect if the guest uses the PIC or the
* IOAPIC. So set the bit in both. The guest will ignore
* writes to the unused one.
*/
idx = srcu_read_lock(&kvm->irq_srcu);
i = kvm_irq_map_gsi(kvm, irq_set, irq);
srcu_read_unlock(&kvm->irq_srcu, idx);
while (i--) {
int r;
r = irq_set[i].set(&irq_set[i], kvm, irq_source_id, level,
line_status);
if (r < 0)
continue;
ret = r + ((ret < 0) ? 0 : ret);
}
return ret;
}
It looks like it finally calls something at:
r = irq_set[i].set(&irq_set[i], kvm, irq_source_id, level,
line_status);
This set function is filled by this.
It sets to this function:
static int vgic_irqfd_set_irq(struct kvm_kernel_irq_routing_entry *e,
struct kvm *kvm, int irq_source_id,
int level, bool line_status)
{
unsigned int spi_id = e->irqchip.pin + VGIC_NR_PRIVATE_IRQS;
if (!vgic_valid_spi(kvm, spi_id))
return -EINVAL;
return kvm_vgic_inject_irq(kvm, 0, spi_id, level, NULL);
}
which calls kvm_vgic_inject_irq which finally calls vgic_put_irq which calls this:
void __vgic_put_lpi_locked(struct kvm *kvm, struct vgic_irq *irq)
{
struct vgic_dist *dist = &kvm->arch.vgic;
if (!kref_put(&irq->refcount, vgic_irq_release))
return;
list_del(&irq->lpi_list);
dist->lpi_list_count--;
kfree(irq);
}
but I don't see how the GIC is called here, I only see the list being deleted.
I thought here it would send the interrupt to the GIC, which would then call the VM somehow.
I'm trying to understand how calling the irqfd file descriptor ends up calling an interrupt in the VM.
VGIC is for arm, you should check arm support file. While x86 is using either APIC or PIC. mostly APIC now.
You can check the specification of how those IRQ chip works to transfer the external signal to the destination core(vcpu).
For example, if you were using a x86 virtual machine(I have no idea of VGIC) which is using IOAPIC, there are 24 pins for example(emulated), and you should understand the APIC(hardware), then you know how it works.
https://elixir.bootlin.com/linux/v5.2.12/source/arch/x86/kvm/irq_comm.c#L271
https://elixir.bootlin.com/linux/v5.2.12/source/arch/x86/kvm/irq_comm.c#L38
I am struggling to find out, what steps are necessary to access a gpio-pin from a linux kernel module.
Maybe someone can explain it to me by a simple example. I like to use pin 4(input) and 33(output). My steps so far:
1.) Device Tree(dts): I leave the dts file untouched - Do I need to setup the pin 4 and 33 via pin control?
2.) kernel module: some pseudo code
gpio_is_valid(4)
gpio_request_one(4, GPIOF_DIR_IN | GPIOF_EXPORT_DIR_FIXED , "myPin4")
gpio_export(4, false)
gpio_get_value(4)
gpio_is_valid(33)
gpio_request_one(33, GPIOF_DIR_OUT | GPIOF_INIT_LOW | GPIOF_OPEN_SOURCE | GPIOF_EXPORT_DIR_FIXED , "myPin33")
gpio_export(33, false)
gpio_set_value(33, 1)
How to do it in a proper way?
I would suggest the combination of an own device tree file + a platform driver + character driver
0.) RTF
check how device trees(dts) are working
check how a platform device works
check how a character device works
gain some knowledge about gpios and dts
#gpio mappings
#subsystems using gpios
#Specifying GPIO information for devices
Read the informations provided by your SOC manufacturer.
The state-of-the-art way to access the gpios is via struct gpio_desc variables. They are created form the device tree.
1.) approach
To toggle a pin under linux you need to make shure, that 3 units are working togehter.
The pin-controller(pinctrl) defines how the output is driven. Open source, pull up etc.
The pin-multiplexer(pinmux) defines different functions for the pin.
The gpio-controller(gpioctrl) translates the gpio number. p.E.: 44 -> GPIO A 11
These components are implemented by the SOC manufacturer. For each platform there are differences. An example for the SAMA5D35xxx follows.
#the device tree
Define the pin controller
pinctrl#fffff200 {
pinctrl_myPins: myPins {
atmel,pins = <AT91_PIOA 2 AT91_PERIPH_GPIO AT91_PINCTRL_NONE // pin 1
AT91_PIOD 19 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>; // pin 2
};
};
Create a node witch is linked to the own platform device:
myPins {
compatible = "myPlatformDevice";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_myPins>;
pin1 = <&pioA 2 GPIO_ACTIVE_HIGH>;
pin2 = <&pioD 19 GPIO_ACTIVE_HIGH>;
};
#create platform + char driver (pseudo code):
// ----------------------
// kernel message support(via dmesg)
// ----------------------
#define KMSG_DEBUG(fmt,args...) printk(KERN_DEBUG "myDrv" ": "fmt"\n", ##args)
#define KMSG_PERR(fmt,args...) printk(KERN_ERR "myDrv" ": "fmt"\n", ##args)
#define KMSG_PINFO(fmt,args...) printk(KERN_INFO "myDrv" ": "fmt"\n", ##args)
// ----------------------
// trace support via defining dMyDrvTrace
// ----------------------
#ifndef dMyDrvTrace
#define TRACE(...)
#else
#define TRACE(fmt,args...) printk(KERN_INFO "myDrv" ": [%s] "fmt"\n", __FUNCTION__, ##args)
#endif
typedef struct SMyDrvDrvData {
struct platform_device *pdev; //!< next device
// here goes the local/private data
int gpiod_pin1;
int gpiod_pin2;
u32 pin1;
u32 pin2;
} TMyDrvDrvData;
static struct dentry * gmyPlattformDrvDebugfsRootDir; //!< root dir at debugfs
static int myPlattformDrv_probe(struct platform_device *pdev);
static int myPlattformDrv_remove(struct platform_device *pdev);
#if defined(CONFIG_OF)
//! filter for the device tree class
static struct of_device_id gMyPlattformDrvdtsFilter[] = {
{.compatible = "myPlatformDevice"},
{}
};
MODULE_DEVICE_TABLE(of, gMyPlattformDrvdtsFilter);
#else
#define gmyPlattformDrvdtsFilter (NULL)
#endif
static struct platform_device *MyPlattformDrv_devs[] = {
};
static struct platform_driver myPlattformDrv_driver = {
.driver = {
.name = dMyPlattformDrvdriver,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(gMyPlattformDrvdtsFilter),
},
.probe = myPlattformDrv_probe,
.remove = myPlattformDrv_remove,
};
// char device
static dev_t gMyCharDev;
static struct class *gMyCharDevClass;
static struct cdev gMyCharDev_cdev;
static int dev_open (struct inode *, struct file *);
static int dev_release (struct inode *, struct file *);
static ssize_t dev_read (struct file *, char *, size_t, loff_t *);
static ssize_t dev_write (struct file *, const char *, size_t, loff_t *);
static const struct file_operations gMyCharDevOps =
{
.read = dev_read,
.open = dev_open,
.write = dev_write,
.release = dev_release
};
//! looks up for the gpio name and request it
static int get_gpio(struct platform_device *pdev, const char * name, int * pGPIOnum)
{
int n,i;
int r;
struct device_node * pDN;
TRACE("look at %s for %s ...", pdev->name, name);
// reset return value
*pGPIOnum = 0;
// parse device tree
// get device tree entries associated with the device
pDN = of_find_node_by_name(NULL, pdev->name);
// parse pins
n = of_gpio_named_count(pDN, name);
if (n <= 0) {
TRACE("no gpios found");
return -1;
}
for (i = 0; i < n; i++) {
// get pin number
*pGPIOnum = of_get_named_gpio(pDN,name, i);
if (*pGPIOnum == -EPROBE_DEFER) {
return r;
}
// check if pin number is valid
if (gpio_is_valid(*pGPIOnum)) {
// yes
// request pin
r = devm_gpio_request(&pdev->dev, *pGPIOnum, name);
if (r) {
return r;
} else {
r = gpio_direction_output(*pGPIOnum, 0);
}
if (r) return r;
}
}
}
return 0;
}
//! probes the platform driver
static int myPlattformDrv_probe(struct platform_device *pdev)
{
struct TMyDrvDrvData *priv;
int i,j,r,gpioNum, ret;
KMSG_PINFO("probe my driver ...");
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
KMSG_PERR("Failed to allocate memory for the private data structure");
return -ENOMEM;
}
priv->pdev = pdev;
platform_set_drvdata(pdev, priv);
TRACE("setup gpios ...");
r = get_gpio(pdev, "pin1", &gpioNum);
if (r) {
KMSG_PERR("Failed to find gpio \"pin1\" in device tree");
}
// save number
priv->gpiod_pin1 = gpioNum;
// create "pin1" debugfs entry
debugfs_create_u32("pin1", S_IRUGO, gmyPlattformDrvDebugfsRootDir, &priv->Pin1);
r = get_gpio(pdev, "pin2", &gpioNum);
if (r) {
KMSG_PERR("Failed to find gpio \"pin2\" in device tree");
}
// save number
priv->gpiod_pin2 = gpioNum;
// create "pin2" debugfs entry
debugfs_create_u32("pin1", S_IRUGO, gmyPlattformDrvDebugfsRootDir, &priv->Pin2);
// create device class
TRACE("create myCharDev char device class");
// create char dev region
ret = alloc_chrdev_region(&gMyCharDev, 0, 1, "myCharDev");
if( ret < 0) {
KMSG_PERR("alloc_chrdev_region error %i", ret);
goto error;
}
// create device class
if((gMyCharDevClass = class_create(THIS_MODULE, dSEK4DevClass)) == NULL)
{
KMSG_PERR("class_create error");
goto error_classCreate;
}
if(NULL == device_create(gMyCharDevClass, NULL, gMyCharDev, NULL, "myCharDev"))
{
KMSG_PERR("device_create error");
goto error_deviceCreate;
}
cdev_init(&gMyCharDev_cdev, &gMyCharDevOps);
ret = cdev_add(&gMyCharDev_cdev, gMyCharDev, 1);
if(-1 == ret) {
KMSG_PERR("cdev_add error %i", ret);
goto error_device_add;
return -1;
}
TRACE("added myCharDev char device");
return 0;
// error handling block
error_std:
error_device_add:
device_destroy(gMyCharDevClass, gMyCharDev);
error_deviceCreate:
class_destroy(gMyCharDevClass);
error_classCreate:
unregister_chrdev_region(gMyCharDev, 1);
error:
return -1;
}
I am developing this module for custom device that, in fact, a 4*8-bit i-o ports attached to ISA bus with addresses 0x0120 - 0x0123. This driver is based on "scull" by Alessandro Rubini and Jonathan Corbet. My OS is Ubuntu 10.04, kernel is 2.6.32-74 generic, I use built-in console-oriented compiler gcc.
While inserting compiled module using "insmod" I get an error "bad address" and module was not loaded. I've tried to debug it using "printk" and found out that my module successfully gets a range of i-o ports, major and minor numbers and then, when trying to do "Reset_Port" function it generates an error "bad address" and exits.
Can anybody tell me, what am I doing wrong?
Here are __exit and __init functions of my module
void __exit ET3201_exit(void)
{
int i;
dev_t devno = MKDEV(ET3201_major, ET3201_minor);
/* Get rid of our char dev entries */
if (ET3201_devices) {
for (i = 0; i < ET3201_nr_devs; i++) {
ET3201_trim(ET3201_devices + i);
cdev_del(&ET3201_devices[i].cdev);
}
kfree(ET3201_devices);
}
#ifdef ET3201_DEBUG /* use proc only if debugging */
ET3201_remove_proc();
#endif
/* cleanup_module is never called if registering failed */
unregister_chrdev_region(devno, ET3201_nr_devs);
if ( ! port ) release_region(BaseIO, 8);
printk(KERN_INFO "Goodbye, cruel world - ET3201 is unloaded\n");
/* and call the cleanup functions for friend devices */
/*ET3201_access_cleanup();*/
}
/*----------------------------------------------------------------------------*/
/* Set up the char_dev structure for this device. */
static void ET3201_setup_cdev(struct ET3201_dev *dev, int index)
{
int err, devno = MKDEV(ET3201_major, ET3201_minor + index);
cdev_init(&dev->cdev, &ET3201_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &ET3201_fops;
dev->CAMAC_Module_Number = CAMAC_Nmod;
dev->CAMAC_Command_Adress = CAMAC_Adcom;
dev->Driver_Number = ET3201_minor + index;
err = cdev_add (&dev->cdev, devno, 1);
/* Fail gracefully if need be */
if (err)
printk(KERN_NOTICE "Error %d adding ET3201%d", err, index);
}
/*----------------------------------------------------------------------------*/
int __init ET3201_init(void)
{
int result = 0;
int i;
dev_t dev = 0;
BaseIO = Base;
/* Get a range of minor numbers to work with, asking for a dynamic
major unless directed otherwise at load time. */
if (ET3201_major) {
dev = MKDEV(ET3201_major, ET3201_minor);
result = register_chrdev_region(dev, ET3201_nr_devs, "ET3201");
} else {
result = alloc_chrdev_region(&dev, ET3201_minor, ET3201_nr_devs, "ET3201");
ET3201_major = MAJOR(dev);
}
if (result < 0) {
printk(KERN_WARNING "ET3201: can't get major %d\n", ET3201_major);
return result;
}
port = request_region(BaseIO, 8, "ET3201");
if ( port == NULL ) {
printk(KERN_WARNING "ET3201 cannot reserve i-o ports %lu \n", BaseIO);
return -ENODEV;
goto fail;
}
/*
* allocate the devices -- we can't have them static, as the number
* can be specified at load time
*/
ET3201_devices = kmalloc(ET3201_nr_devs * sizeof(struct ET3201_dev), GFP_KERNEL);
if (! ET3201_devices) {
result = -ENOMEM;
printk(KERN_ALERT "ET3201: can't get memory \n");
goto fail; /* Fail gracefully if need be */
}
memset(ET3201_devices, 0, ET3201_nr_devs * sizeof(struct ET3201_dev));
/* Initialize each device. */
for (i = 0; i < ET3201_nr_devs; i++) {
ET3201_devices[i].quantum = ET3201_quantum;
ET3201_devices[i].qset = ET3201_qset;
init_MUTEX(&ET3201_devices[i].sem);
ET3201_setup_cdev(&ET3201_devices[i], i);
}
/* At this point call the init function for any friend device */
dev = MKDEV(ET3201_major, ET3201_minor + ET3201_nr_devs);
/*dev += ET3201_access_init(dev);*/
printk(KERN_INFO "ET3201 is initialized with major %d\n", ET3201_major);
if ( port != NULL ){
printk(KERN_INFO "ET3201 is trying to reset %d devices\n", ET3201_nr_devs);
result = Reset_Port();
}
if ( result != 0 ) {
printk(KERN_ALERT "ET3201: device cannot reset with result %d\n", result);
result = -EFAULT;
goto fail;
}
#ifdef ET3201_DEBUG /* only when debugging */
ET3201_create_proc();
#endif
return 0; /* succeed */
fail:
ET3201_exit();
return result;
}
/*----------------------------------------------------------------------------*/
module_init(ET3201_init);
module_exit(ET3201_exit);
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(ET3201_minor);
and next will be Reset_Port()
static int Reset_Port(void)
{
int result = -EIO;
int count;
if (port == NULL) goto fail;
for ( count = 0; count < ET3201_nr_devs; count++ )
{
outb(0x00, ports[count]);
}
wmb(); /*write memory barrier*/
LastOp = E_Reset;
result = 0; /* success */
fail:
return result;
}
EXPORT_SYMBOL(Reset_Port);
Now, after fixing 'int Reset_Port(void)' I've got another problem -
'WARNING: modpost: Found 1 section mismatch(es).'
After debugging I see that this is a result of calling 'ET3201_exit()'
from 'module_init()' - when I remarked this call, warning disappeared.
Surprising that exactly the same call was made in "scull" driver of respected authors - and it works.
Question: What can lead to kernel mismatch in this code?
Yes! The bug is fixed - after declaring ' int Reset_Port(void) ' the module was inserted and removed successfully. I thought,(but it was wrong) that all functions that can be called from within ' module_init() ' must not be declared as static.
I am new with Linux.
I have made a USB skeleton driver and one application program which open and close skeleton.
But it gives error can't open device.
Can anyone tell me the possible reason why this may happen?
This simple driver programs needs any device attached with usb port ?
Here is my application programs
int main()
/* no memory-swapping for this programm */
ret = mlockall(MCL_CURRENT | MCL_FUTURE);
if (ret) {
perror("ERROR : mlockall has failled");
exit(1);
}
/*
* Turn the NRTcurrent task into a RT-task.
* */
ret = rt_task_shadow(&rt_task_desc, NULL, 1, 0);
if (ret)
{
fprintf(stderr, "ERROR : rt_task_shadow: %s\n",
strerror(-ret));
exit(1);
}
/* open the device */
device = rt_dev_open(DEVICE_NAME, 0);
if (device < 0) {
printf("ERROR : can't open device %s (%s)\n",
DEVICE_NAME, strerror(-device));
exit(1);
}
/*
* If an argument was given on the command line, write it to the device,
* otherwise, read from the device.
*/
/* close the device */
ret = rt_dev_close(device);
if (ret < 0) {
printf("ERROR : can't close device %s (%s)\n",
DEVICE_NAME, strerror(-ret));
exit(1);
}
return 0;
}
Here is a my driver open function
static int skel_open(struct inode *inode, struct file *file)
{
struct usb_skel *dev;
struct usb_interface *interface;
int subminor;
int retval = 0;
subminor = iminor(inode);
interface = usb_find_interface(&skel_driver, subminor);
if (!interface) {
pr_err("%s - error, can't find device for minor %d\n",
__func__, subminor);
retval = -ENODEV;
goto exit;
}
dev = usb_get_intfdata(interface);
if (!dev) {
retval = -ENODEV;
goto exit;
}
/* increment our usage count for the device */
kref_get(&dev->kref);
/* lock the device to allow correctly handling errors
* in resumption */
mutex_lock(&dev->io_mutex);
retval = usb_autopm_get_interface(interface);
if (retval)
goto out_err;
/* save our object in the file's private structure */
file->private_data = dev;
mutex_unlock(&dev->io_mutex);
exit:
return retval;
}
This is my first post so please let me know if there is any mistake from .
My aim is to get approx 150MBytes of data transfer from KERNEL to user space.
[This is because i am building an driver for DMA device on OMAP l138 to transfer and receive data between DMA DEVICE and FPGA]
Now in LINUX KERNEL i am allocating BUFFER of VARIABLE size using dma_alloc_coherent
Then the PHYSICAL address of this buffer i am passing to user space to be user as
OFFSET parameter to be used for mmap call from user space .
Then from data is copied and read back to and from from user space to kernel
This logic work fine till size of buffer is 4096. Above 4k the mmap fails and return "MAP_FAILED"
static int driver_mmap(struct file *f, struct vm_area_struct *vma)
{
u32bit ret;
u32bit size = (vma->vm_end)-(vma->vm_start);
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
if (size > (NUM_PAGE*PAGE_SIZE)){
return(-1);
}
if ((ret = remap_pfn_range(vma,vma->vm_start,
(virt_to_phys((void *)krnl_area) >> PAGE_SHIFT),
size,vma->vm_page_prot)) < 0)
{
return ret;
}
printk("\nDVR:The MMAP returned %x to USER SAPCE \n",ret);
return 0;
}
//MMAP STEP 1
dmasrc_ptr = dma_alloc_coherent( NULL ,GLOBAL_BUFFER_SIZE , &dmasrc ,0);
if( !dmasrc_ptr ) {
printk(KERN_INFO "DMA_ALLOC_FAILED for the source buffer ...\n");
return -ENOMEM;
}else{
printk( "\n--->The address of SRC is %x..\n",dmasrc_ptr);
}
temp_source=dmasrc_ptr;
//MMAP STEP 2
// Round the allocated KERNEL MEMORY to the page bondary
krnl_area=(int *)((((unsigned long)dmasrc_ptr) + PAGE_SIZE - 1)&PAGE_MASK);
printk(KERN_CRIT "DVR:The KERNEL VIRTUAL ADDRS is %x..\n",krnl_area);
//MMAP STEP 3
// Marking the PAGES as RESERVED
for (i = 0; i < (NUM_PAGE * PAGE_SIZE); i+= PAGE_SIZE) {
SetPageReserved(virt_to_page(((unsigned long)krnl_area) + i));
//Application code part
while(1){
fflush(stdin);
fflush(stdout);
printf("\n\n\n----------------------------------------------------\n");
printf("USR:Please enter your requirement ");
printf("\n----------------------------------------------------\n");
printf("\t1----->GET_UPP_OFFSET\n");
printf("\t2----->UPP_MMAP_CALL\n");
printf("\t3----->IOCTL_UPP_WRITE\n");
printf("\t4----->IOCTL_UPP_READ\n");
printf("\n");
scanf("%d",&option);
printf("\nThe OPTION is %d..\n",option);
printf("\n");
switch(option){
case 1 :
{
offset=0;
ret = ioctl(dev_FD ,IOCTL_UPP_START, &info);
if (ret < 0) {
printf("dma buffer info ioctl failed\n");
}
offset = info.var;
printf("THE ADDRESS WE GOT IS %X..\n",offset);
}
break;
case 2 :
{
printf("THE OFFSET is %X..\n",offset);
mmap_Ptr= mmap(0,BUFFER_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, dev_FD, 0);
if (mmap_Ptr == MAP_FAILED){
printf("USR[UPP] :MMAP FAiled \n\n");
close(dev_FD);
exit(-1);
}
printf("THE MMAP address is %X..\n",mmap_Ptr);
}
break;
case 3:
{
struct upp_struct user_local_struct;
printf("\n***************************************************\n");
for (i = 0; i <(1024);i++) {
*(mmap_Ptr+i)=test_var;
printf("WR:%X ",*(mmap_Ptr+i));
//test_var++;
}
ioctl(dev_FD , IOCTL_UPP_WRITE ,&user_local_struct);
printf("\n***************************************************\n\n\n");
for(i=0;i<20402;i++){
//NOP
}
//test_var=0x00;
}
break;
case 4:
{
struct upp_struct user_local_struct;
ioctl(dev_FD , IOCTL_UPP_READ,&user_local_struct);
for(i=0;i<20402;i++){
//NOP
}
printf("\n***************************************************\n");
for (i = 0; i <(1024);i++) {
printf("RD:%X",*(mmap_Ptr+i));
}
printf("\n***************************************************\n\n\n");
}
break;
default:
{
printf("USR:You have entered an wrong option \n");
printf("\nUSR:CLosing the FILE ENTERIES ...\n");
munmap(mmap_Ptr,BUFFER_SIZE);
free(source_ptr);
free(dest_ptr);
close(dev_FD);
exit(0);
}
break;
} //END OF SWITCH LOOP
} //END OF WHILE LOOP
use get_free_pages to allocate multiple pages, or use vmalloc but you need call remap_pfn_range at every page basis as vmalloc-ed physical memory could be not physically continuous.