我正在尝试为 Gumstix Overo Fire 的 Angstrom Linux 2.6.36 编写一个 SPI 驱动程序。我的驱动程序在中断处理程序中不断崩溃。这是完整的代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/cdev.h>
#include <linux/spi/spi.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include <linux/kernel.h>
#include <mach/gpio.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/hrtimer.h>
#define IRQ_PIN 10
#define SPI_BUFF_SIZE 4
#define USER_BUFF_SIZE 128
#define SPI_BUS 1
#define SPI_BUS_CS1 1
#define SPI_BUS_SPEED 1500000
unsigned char *buff_even = 0;
unsigned char *buff_odd = 0;
unsigned char *temp_buff = 0;
unsigned int sample_counter = 0;
unsigned int buff_counter = 0;
unsigned int current_buffer = 0;
unsigned int local_current_buffer = 0;
unsigned int local_sample_counter = 0;
unsigned int num_reads = 0;
unsigned int num_miss_samples = 0;
unsigned int regval = 0;
#define LIMIT (4000)
#define BUFF_SIZE (4*LIMIT)
#define MAJOR_NUM 100
#define READ_CURR_COUNTER _IOWR(MAJOR_NUM, 1, int)
#define READ_BUFF _IOWR(MAJOR_NUM, 2, int)
#define READ_CURR_BUFF_NO _IOWR(MAJOR_NUM, 3, int)
#define READ_REGISTER _IOWR(MAJOR_NUM, 4, unsigned char)
#define WRITE_REGISTER _IOWR(MAJOR_NUM, 5, int)
#define START_READ _IOWR(MAJOR_NUM, 6, int)
#define STOP_READ _IOWR(MAJOR_NUM, 7, int)
const char this_driver_name[] = "adc";
static int running = 0;
static int resetting = 0;
static int reading = 0;
struct spike_control
{
struct spi_message msg;
struct spi_transfer transfer;
u8 *tx_buff;
u8 *rx_buff;
};
static struct spike_control spike_ctl;
struct spike_dev
{
struct semaphore spi_sem;
struct semaphore fop_sem;
dev_t devt;
struct cdev cdev;
struct class *class;
struct spi_device *spi_device;
char *user_buff;
u8 test_data;
int irq;
};
static struct spike_dev spike_dev;
static DEFINE_MUTEX(list_lock);
static DEFINE_MUTEX(count_lock);
static int status;
static void spike_completion_handler(void *arg)
{
local_sample_counter++;
if (sample_counter >= local_sample_counter + 1)
num_miss_samples++;
if (current_buffer == 0)
{
buff_even[buff_counter++] = spike_ctl.rx_buff[0];
buff_even[buff_counter++] = spike_ctl.rx_buff[1];
buff_even[buff_counter++] = spike_ctl.rx_buff[2];
buff_even[buff_counter++] = spike_ctl.rx_buff[3];
}
else if (current_buffer == 1)
{
buff_odd[buff_counter++] = spike_ctl.rx_buff[0];
buff_odd[buff_counter++] = spike_ctl.rx_buff[1];
buff_odd[buff_counter++] = spike_ctl.rx_buff[2];
buff_odd[buff_counter++] = spike_ctl.rx_buff[3];
}
memset(spike_ctl.rx_buff, 0, SPI_BUFF_SIZE);
if (sample_counter == LIMIT)
{
buff_counter = 0;
mutex_lock(&count_lock);
if (current_buffer == 0)
current_buffer = 1;
else
current_buffer = 0;
sample_counter = 0;
mutex_unlock(&count_lock);
local_sample_counter = 0;
}
}
static irqreturn_t adc_handler(int irq, void *dev_id)
{
sample_counter++;
spi_message_init(&spike_ctl.msg);
spike_ctl.msg.complete = spike_completion_handler;
spike_ctl.msg.context = NULL;
spike_ctl.transfer.tx_buf = NULL;
spike_ctl.transfer.rx_buf = spike_ctl.rx_buff;
spike_ctl.transfer.len = 4;
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_async(spike_dev.spi_device, &spike_ctl.msg);
return IRQ_HANDLED;
}
static void resetbuffers(void)
{
local_sample_counter = 0;
sample_counter = 0;
num_miss_samples = 0;
current_buffer = 0;
buff_counter = 0;
memset(spike_ctl.rx_buff, 0, SPI_BUFF_SIZE);
memset(spike_ctl.tx_buff, 0, SPI_BUFF_SIZE);
memset(buff_even, 0, BUFF_SIZE);
memset(buff_odd, 0, BUFF_SIZE);
memset(temp_buff, 0, BUFF_SIZE);
}
static int read_register(unsigned char addr)
{
if (down_interruptible(&spike_dev.spi_sem))
return -ERESTARTSYS;
if (!spike_dev.spi_device)
{
up(&spike_dev.spi_sem);
return -ENODEV;
}
memset(spike_ctl.rx_buff, 0, SPI_BUFF_SIZE);
memset(spike_ctl.tx_buff, 0, SPI_BUFF_SIZE);
spike_ctl.transfer.tx_buf = spike_ctl.tx_buff;
spike_ctl.transfer.rx_buf = spike_ctl.rx_buff;
spike_ctl.transfer.len = 1;
spike_ctl.tx_buff[0] = 0x11;//Stop read data continuous
spi_message_init(&spike_ctl.msg);
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
__asm__ __volatile__("ldr r0,=0x2710\n\t"
".LOOP1:\n\t"
"subs r0,r0,#1\n\t"
"bne .LOOP1\n\t");
spike_ctl.tx_buff[0] = 0x20 + addr;//Address of register
spi_message_init(&spike_ctl.msg);
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
__asm__ __volatile__("ldr r0,=0x2710\n\t"
".LOOP2:\n\t"
"subs r0,r0,#1\n\t"
"bne .LOOP2\n\t");
spike_ctl.tx_buff[0] = 0;//Number of registers to read minus 1
spi_message_init(&spike_ctl.msg);
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
__asm__ __volatile__("ldr r0,=0x2710\n\t"
".LOOP3:\n\t"
"subs r0,r0,#1\n\t"
"bne .LOOP3\n\t");
spi_message_init(&spike_ctl.msg);
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
__asm__ __volatile__("ldr r0,=0x2710\n\t"
".LOOP4:\n\t"
"subs r0,r0,#1\n\t"
"bne .LOOP4\n\t");
printk(KERN_ALERT "%x\n",spike_ctl.rx_buff[0]);
regval = spike_ctl.rx_buff[0];
// spike_ctl.tx_buff[0] = 0x10;//Start read data continuous
// spi_message_init(&spike_ctl.msg);
// spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
// status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
up(&spike_dev.spi_sem);
return status;
}
static int write_register(unsigned char addr, unsigned char val)
{
if (down_interruptible(&spike_dev.spi_sem))
return -ERESTARTSYS;
if (!spike_dev.spi_device)
{
up(&spike_dev.spi_sem);
return -ENODEV;
}
memset(spike_ctl.rx_buff, 0, SPI_BUFF_SIZE);
memset(spike_ctl.tx_buff, 0, SPI_BUFF_SIZE);
spike_ctl.transfer.tx_buf = spike_ctl.tx_buff;
spike_ctl.transfer.rx_buf = spike_ctl.rx_buff;
spike_ctl.transfer.len = 1;
spike_ctl.tx_buff[0] = 0x11;//Stop read data continuous
spi_message_init(&spike_ctl.msg);
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
__asm__ __volatile__("ldr r0,=0x2710\n\t"
".LOOP5:\n\t"
"subs r0,r0,#1\n\t"
"bne .LOOP5\n\t");
spike_ctl.tx_buff[0] = 0x40 + addr;//Address of register
spi_message_init(&spike_ctl.msg);
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
__asm__ __volatile__("ldr r0,=0x2710\n\t"
".LOOP6:\n\t"
"subs r0,r0,#1\n\t"
"bne .LOOP6\n\t");
spike_ctl.tx_buff[0] = 0;//Number of registers to read minus 1
spi_message_init(&spike_ctl.msg);
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
__asm__ __volatile__("ldr r0,=0x2710\n\t"
".LOOP7:\n\t"
"subs r0,r0,#1\n\t"
"bne .LOOP7\n\t");
spike_ctl.tx_buff[0] = val;//Value to write
spi_message_init(&spike_ctl.msg);
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
__asm__ __volatile__("ldr r0,=0x2710\n\t"
".LOOP8:\n\t"
"subs r0,r0,#1\n\t"
"bne .LOOP8\n\t");
spike_ctl.tx_buff[0] = 0x10;//Start read data continuous
spi_message_init(&spike_ctl.msg);
spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
// spike_ctl.tx_buff[0] = 0x10;//Start read data continuous
// spi_message_init(&spike_ctl.msg);
// spi_message_add_tail(&spike_ctl.transfer, &spike_ctl.msg);
// status = spi_sync(spike_dev.spi_device, &spike_ctl.msg);
up(&spike_dev.spi_sem);
return status;
}
static long adc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
unsigned char addr;
switch(cmd)
{
case READ_CURR_COUNTER:
mutex_lock(&count_lock);
local_current_buffer = current_buffer;
local_sample_counter = sample_counter;
mutex_unlock(&count_lock);
copy_to_user((void *)arg, &local_sample_counter, sizeof(unsigned int));
printk(KERN_ALERT "Read current counter %d\n",local_sample_counter);
break;
case READ_BUFF:
mutex_lock(&count_lock);
local_current_buffer = current_buffer;
local_sample_counter = sample_counter;
mutex_unlock(&count_lock);
if (local_current_buffer == 0)
{
memcpy(temp_buff,buff_odd, BUFF_SIZE);
// memset(temp_buff, 'Q', BUFF_SIZE);
}
else
{
memcpy(temp_buff,buff_even, BUFF_SIZE);
// memset(temp_buff, 'T', BUFF_SIZE);
}
copy_to_user((void *)arg, temp_buff, BUFF_SIZE);
num_reads++;
break;
case READ_CURR_BUFF_NO:
mutex_lock(&count_lock);
local_current_buffer = current_buffer;
local_sample_counter = sample_counter;
mutex_unlock(&count_lock);
copy_to_user((void *)arg, &local_current_buffer, sizeof(int));
break;
case READ_REGISTER:
get_user(addr, (unsigned char *)arg);
disable_irq(spike_dev.irq);
read_register(addr);
enable_irq(spike_dev.irq);
copy_to_user((void *)arg, ®val, sizeof(int));
break;
case WRITE_REGISTER:
disable_irq(spike_dev.irq);
enable_irq(spike_dev.irq);
//copy_to_user((void *)arg, &local_current_buffer, sizeof(int));
break;
default:
return -ENOTTY;
}
return 1;
}
static int spike_open(struct inode *inode, struct file *filp)
{
int status = 0;
if (down_interruptible(&spike_dev.fop_sem))
return -ERESTARTSYS;
if (!spike_dev.user_buff) {
spike_dev.user_buff = kmalloc(USER_BUFF_SIZE, GFP_KERNEL);
if (!spike_dev.user_buff)
status = -ENOMEM;
}
up(&spike_dev.fop_sem);
return status;
}
static int spike_probe(struct spi_device *spi_device)
{
if (down_interruptible(&spike_dev.spi_sem))
return -EBUSY;
spike_dev.spi_device = spi_device;
printk(KERN_ALERT "SPI[%d] max_speed_hz %d Hz\n", spi_device->chip_select, spi_device->max_speed_hz);
up(&spike_dev.spi_sem);
return 0;
}
static int spike_remove(struct spi_device *spi_device)
{
if (down_interruptible(&spike_dev.spi_sem))
return -EBUSY;
spike_dev.spi_device = NULL;
up(&spike_dev.spi_sem);
return 0;
}
static int __init add_spike_device_to_bus(void)
{
struct spi_master *spi_master;
struct spi_device *spi_device;
struct device *pdev;
char buff[64];
int status = 0;
spi_master = spi_busnum_to_master(SPI_BUS);
if (!spi_master)
{
printk(KERN_ALERT "spi_busnum_to_master(%d) returned NULL\n",
SPI_BUS);
printk(KERN_ALERT "Missing modprobe omap2_mcspi?\n");
return -1;
}
spi_device = spi_alloc_device(spi_master);
if (!spi_device)
{
put_device(&spi_master->dev);
printk(KERN_ALERT "spi_alloc_device() failed\n");
return -1;
}
spi_device->chip_select = SPI_BUS_CS1;
/* Check whether this SPI bus.cs is already claimed */
snprintf(buff, sizeof(buff), "%s.%u",
dev_name(&spi_device->master->dev),
spi_device->chip_select);
pdev = bus_find_device_by_name(spi_device->dev.bus, NULL, buff);
if (pdev)
{
/* We are not going to use this spi_device, so free it */
spi_dev_put(spi_device);
/*
* There is already a device configured for this bus.cs
* It is okay if it us, otherwise complain and fail.
*/
if (pdev->driver && pdev->driver->name && strcmp(this_driver_name, pdev->driver->name))
{
printk(KERN_ALERT
"Driver [%s] already registered for %s\n",
pdev->driver->name, buff);
status = -1;
}
}
else
{
spi_device->max_speed_hz = SPI_BUS_SPEED;
spi_device->mode = SPI_MODE_0;
spi_device->bits_per_word = 8;
spi_device->irq = -1;
spi_device->controller_state = NULL;
spi_device->controller_data = NULL;
strlcpy(spi_device->modalias, this_driver_name, SPI_NAME_SIZE);
status = spi_add_device(spi_device);
if (status < 0)
{
spi_dev_put(spi_device);
printk(KERN_ALERT "spi_add_device() failed: %d\n",
status);
}
}
put_device(&spi_master->dev);
return status;
}
static struct spi_driver spike_driver = {
.driver = {
.name = this_driver_name,
.owner = THIS_MODULE,
},
.probe = spike_probe,
.remove = __devexit_p(spike_remove),
};
static int __init spike_init_spi(void)
{
int error;
spike_ctl.tx_buff = kmalloc(SPI_BUFF_SIZE, GFP_KERNEL | GFP_DMA);
if (!spike_ctl.tx_buff)
{
error = -ENOMEM;
goto spike_init_error;
}
spike_ctl.rx_buff = kmalloc(SPI_BUFF_SIZE, GFP_KERNEL | GFP_DMA);
if (!spike_ctl.rx_buff)
{
error = -ENOMEM;
goto spike_init_error;
}
error = spi_register_driver(&spike_driver);
if (error < 0)
{
printk(KERN_ALERT "spi_register_driver() failed %d\n", error);
goto spike_init_error;
}
error = add_spike_device_to_bus();
if (error < 0)
{
printk(KERN_ALERT "add_spike_to_bus() failed\n");
spi_unregister_driver(&spike_driver);
goto spike_init_error;
}
spike_dev.irq = OMAP_GPIO_IRQ(IRQ_PIN);
return 0;
spike_init_error:
if (spike_ctl.tx_buff) {
kfree(spike_ctl.tx_buff);
spike_ctl.tx_buff = 0;
}
if (spike_ctl.rx_buff) {
kfree(spike_ctl.rx_buff);
spike_ctl.rx_buff = 0;
}
return error;
}
static ssize_t spike_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
size_t len;
ssize_t status = 0;
if (!buff)
return -EFAULT;
if (*offp > 0)
return 0;
if (down_interruptible(&spike_dev.fop_sem))
return -ERESTARTSYS;
printk(KERN_ALERT "Interrupt triggered %d Missed packet %d\n", sample_counter, num_miss_samples);
up(&spike_dev.fop_sem);
return status;
}
static ssize_t spike_write(struct file *filp, const char __user *buff, size_t count, loff_t *f_pos)
{
size_t len;
ssize_t status = 0;
if (down_interruptible(&spike_dev.fop_sem))
return -ERESTARTSYS;
memset(spike_dev.user_buff, 0, 16);
len = count > 8 ? 8 : count;
if (copy_from_user(spike_dev.user_buff, buff, len))
{
status = -EFAULT;
goto spike_write_done;
}
/* we'll act as if we looked at all the data */
status = count;
/* but we only care about the first 5 characters */
if (!strnicmp(spike_dev.user_buff, "inc", 3))
{
disable_irq(spike_dev.irq);
write_register(1,0x62);
resetbuffers();
printk(KERN_ALERT "4000 samples per second\n");
enable_irq(spike_dev.irq);
}
if (!strnicmp(spike_dev.user_buff, "dec", 3))
{
disable_irq(spike_dev.irq);
write_register(1,0x52);
resetbuffers();
printk(KERN_ALERT "1000 samples per second\n");
enable_irq(spike_dev.irq);
}
if (!strnicmp(spike_dev.user_buff, "stop", 4))
{
disable_irq(spike_dev.irq);
resetbuffers();
printk(KERN_ALERT "Driver stopped\n");
}
spike_write_done:
up(&spike_dev.fop_sem);
return status;
}
static const struct file_operations spike_fops = {
.owner = THIS_MODULE,
.open = spike_open,
.read = spike_read,
.write = spike_write,
.unlocked_ioctl = adc_ioctl,
};
static int __init spike_init_cdev(void)
{
int error;
spike_dev.devt = MKDEV(0, 0);
error = alloc_chrdev_region(&spike_dev.devt, 0, 1, this_driver_name);
if (error < 0)
{
printk(KERN_ALERT "alloc_chrdev_region() failed: %d \n",
error);
return -1;
}
cdev_init(&spike_dev.cdev, &spike_fops);
spike_dev.cdev.owner = THIS_MODULE;
error = cdev_add(&spike_dev.cdev, spike_dev.devt, 1);
if (error)
{
printk(KERN_ALERT "cdev_add() failed: %d\n", error);
unregister_chrdev_region(spike_dev.devt, 1);
return -1;
}
return 0;
}
static int __init spike_init_class(void)
{
spike_dev.class = class_create(THIS_MODULE, this_driver_name);
if (!spike_dev.class)
{
printk(KERN_ALERT "class_create() failed\n");
return -1;
}
if (!device_create(spike_dev.class, NULL, spike_dev.devt, NULL, this_driver_name))
{
printk(KERN_ALERT "device_create(..., %s) failed\n",
this_driver_name);
class_destroy(spike_dev.class);
return -1;
}
return 0;
}
static int __init spike_init(void)
{
int result;
memset(&spike_dev, 0, sizeof(spike_dev));
memset(&spike_ctl, 0, sizeof(spike_ctl));
sema_init(&spike_dev.spi_sem, 1);
sema_init(&spike_dev.fop_sem, 1);
buff_even = kmalloc(BUFF_SIZE, GFP_KERNEL);
buff_odd = kmalloc(BUFF_SIZE, GFP_KERNEL);
temp_buff = kmalloc(BUFF_SIZE, GFP_KERNEL);
if ( buff_even == 0 )
printk(KERN_ALERT "Failed to allocate buffer even\n");
if ( buff_odd == 0 )
printk(KERN_ALERT "Failed to allocate buffer odd\n");
if ( temp_buff == 0 )
printk(KERN_ALERT "Failed to temp buffer\n");
if (spike_init_cdev() < 0)
goto fail_1;
if (spike_init_class() < 0)
goto fail_2;
if (spike_init_spi() < 0)
goto fail_3;
result = request_irq(spike_dev.irq, adc_handler, IRQF_TRIGGER_RISING, "adc", &spike_dev);
if (result < 0)
{
printk(KERN_ALERT "request_irq failed: %d\n", result);
return -1;
}
return 0;
fail_3:
device_destroy(spike_dev.class, spike_dev.devt);
class_destroy(spike_dev.class);
fail_2:
cdev_del(&spike_dev.cdev);
unregister_chrdev_region(spike_dev.devt, 1);
fail_1:
return -1;
}
module_init(spike_init);
static void __exit spike_exit(void)
{
disable_irq(spike_dev.irq);
free_irq(spike_dev.irq, &spike_dev);
gpio_free(IRQ_PIN);
spi_unregister_device(spike_dev.spi_device);
spi_unregister_driver(&spike_driver);
device_destroy(spike_dev.class, spike_dev.devt);
class_destroy(spike_dev.class);
cdev_del(&spike_dev.cdev);
unregister_chrdev_region(spike_dev.devt, 1);
if (spike_ctl.tx_buff)
kfree(spike_ctl.tx_buff);
if (spike_ctl.rx_buff)
kfree(spike_ctl.rx_buff);
if (spike_dev.user_buff)
kfree(spike_dev.user_buff);
if ( buff_even != 0 )
kfree(buff_even);
if ( buff_odd != 0 )
kfree(buff_odd);
if ( temp_buff != 0 )
kfree(temp_buff);
printk(KERN_ALERT "Interrupt triggered %d Missed packet %d\n", sample_counter, num_miss_samples);
}
module_exit(spike_exit);
中断处理程序崩溃在线
status = spi_async(spike_dev.spi_device, &spike_ctl.msg);
如果我注释掉这一行,一切运行正常,但当然没有从 SPI 读取数据。