杂项设备驱动
我们复制一个以前的驱动文件修改
主要是两步
1.填充结构体 miscdevice
这个结构体我们就用前三个成员,如下
2.注册杂项设备、生成设备结点
misc_register 参数是我们上个结构体的地址
misc_deregister删除设备,驱动注销时也要写上
编译
make uImage --------》 板子运行
然后就可以向板子上添加了
自动创建设备结点
主要用到两个函数
1.class_create
2.device_create 导出设备信息到用户空间
如下
此外,提供一种自动创建设备号的方法
示例
adc_automknod.c
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <mach/irqs.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#define MAJOR_NUM 247
#define MINOR_NUM 0
#define DEV_NAME "adc2"
static wait_queue_head_t wq;
static int condition = 0;
#define ADCCON 0x58000000
#define ADCDAT0 0x5800000C
#define CLKCON 0x4C00000C
static volatile unsigned long * adccon;
static volatile unsigned long * adcdat0;
static volatile unsigned long * clkcon;
static void adc_init(void)
{
*adccon = (1 << 14) | (49 << 6);
}
static void adc_start(void)
{
*adccon |= (1 << 0);
}
static unsigned short adc_read(void)
{
unsigned short adc_value = *adcdat0 & 0x3ff;
return adc_value;
}
static irqreturn_t adc_handler(int irq, void * arg)
{
condition = 1;
wake_up_interruptible(&wq);
printk("irq = %d\n", irq);
return IRQ_HANDLED;
}
static int open(struct inode * node, struct file * file)
{
adc_init();
printk("kernel open \n");
return 0;
}
static ssize_t read(struct file * file, char __user * buf, size_t len, loff_t * loff)
{
unsigned short data = 0;
adc_start();
condition = 0;
wait_event_interruptible(wq, condition);
data = adc_read();
copy_to_user(buf, &data, sizeof(data));
printk("kernel read\n");
return sizeof(data);
}
static ssize_t write(struct file * file, const char __user * buf, size_t len, loff_t * loff)
{
printk("kernel write \n");
return 0;
}
static int close(struct inode * node, struct file * file)
{
printk("kernel close \n");
return 0;
}
static dev_t dev_num;
static struct file_operations fops =
{
.owner = THIS_MODULE,
.open = open,
.read = read,
.write = write,
.release = close
};
static struct cdev dev;
static struct class * pcls;
struct device * pdev;
static int __init adc2_init(void)
{
int ret = 0;
dev_num = MKDEV(MAJOR_NUM, MINOR_NUM); // MAJOR_NUM << 20 | MINOR_NUM;
ret = cdev_add(&dev, dev_num, 1);
if(ret < 0)
goto err_add_failed;
cdev_init(&dev, &fops);
ret = register_chrdev_region(dev_num, 1, DEV_NAME);
if(ret < 0)
goto err2_register_failed;
pcls = class_create(THIS_MODULE, "class_adc");
if(NULL == pcls)
goto err_class_create_failed;
pdev = device_create(pcls, NULL, dev_num, NULL, DEV_NAME);
if(NULL == pdev)
goto err_device_create_failed;
ret = request_irq(IRQ_ADC, adc_handler, IRQF_TRIGGER_FALLING | IRQF_DISABLED, "adc_irq", NULL);
if(ret < 0)
goto err_request_irq_failed;
init_waitqueue_head(&wq);
adccon = ioremap(ADCCON, sizeof(adccon));
adcdat0 = ioremap(ADCDAT0, sizeof(adcdat0));
clkcon = ioremap(CLKCON, sizeof(clkcon));
*clkcon |= (1 << 15);
printk("clkcon = %lx\n", *clkcon);
printk("adc_init ############################\n");
return 0;
err_add_failed:
printk("cdev_add failed\n");
cdev_del(&dev);
return ret;
err2_register_failed:
printk("register_chrdev_region failed\n");
unregister_chrdev_region(dev_num, 1);
cdev_del(&dev);
return ret;
err_class_create_failed:
printk("class_create failed\n");
class_destroy(pcls);
unregister_chrdev_region(dev_num, 1);
cdev_del(&dev);
return ret;
err_device_create_failed:
printk("device_create failed\n");
device_destroy(pcls, dev_num);
class_destroy(pcls);
unregister_chrdev_region(dev_num, 1);
cdev_del(&dev);
return ret;
err_request_irq_failed:
printk("request_irq failed\n");
disable_irq(IRQ_ADC);
device_destroy(pcls, dev_num);
class_destroy(pcls);
free_irq(IRQ_ADC, NULL);
unregister_chrdev_region(dev_num, 1);
cdev_del(&dev);
return ret;
}
static void __exit adc_exit(void)
{
iounmap(clkcon);
iounmap(adcdat0);
iounmap(adccon);
device_destroy(pcls, dev_num);
class_destroy(pcls);
disable_irq(IRQ_ADC);
free_irq(IRQ_ADC, NULL);
unregister_chrdev_region(dev_num, 1);
cdev_del(&dev);
printk("adc_exit ...\n");
}
module_init(adc2_init);
module_exit(adc_exit);
MODULE_LICENSE("GPL");
adc_misc.c
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <mach/irqs.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/miscdevice.h>
#define DEV_NAME "adc2"
static wait_queue_head_t wq;
static int condition = 0;
#define ADCCON 0x58000000
#define ADCDAT0 0x5800000C
#define CLKCON 0x4C00000C
static volatile unsigned long * adccon;
static volatile unsigned long * adcdat0;
static volatile unsigned long * clkcon;
static void adc_init(void)
{
*adccon = (1 << 14) | (49 << 6);
}
static void adc_start(void)
{
*adccon |= (1 << 0);
}
static unsigned short adc_read(void)
{
unsigned short adc_value = *adcdat0 & 0x3ff;
return adc_value;
}
static irqreturn_t adc_handler(int irq, void * arg)
{
condition = 1;
wake_up_interruptible(&wq);
printk("irq = %d\n", irq);
return IRQ_HANDLED;
}
static int open(struct inode * node, struct file * file)
{
adc_init();
printk("kernel open \n");
return 0;
}
static ssize_t read(struct file * file, char __user * buf, size_t len, loff_t * loff)
{
unsigned short data = 0;
adc_start();
condition = 0;
wait_event_interruptible(wq, condition);
data = adc_read();
copy_to_user(buf, &data, sizeof(data));
printk("kernel read\n");
return sizeof(data);
}
static ssize_t write(struct file * file, const char __user * buf, size_t len, loff_t * loff)
{
printk("kernel write \n");
return 0;
}
static int close(struct inode * node, struct file * file)
{
printk("kernel close \n");
return 0;
}
static struct file_operations fops =
{
.owner = THIS_MODULE,
.open = open,
.read = read,
.write = write,
.release = close
};
static struct miscdevice misc_d =
{
.minor = MISC_DYNAMIC_MINOR,
.name = DEV_NAME,
.fops = &fops
};
static int __init adc2_init(void)
{
int ret = misc_register(&misc_d);
if(ret < 0)
goto err_misc_register_failed;
ret = request_irq(IRQ_ADC, adc_handler, IRQF_TRIGGER_FALLING | IRQF_DISABLED, "adc_irq", NULL);
if(ret < 0)
goto err_request_irq_failed;
init_waitqueue_head(&wq);
adccon = ioremap(ADCCON, sizeof(adccon));
adcdat0 = ioremap(ADCDAT0, sizeof(adcdat0));
clkcon = ioremap(CLKCON, sizeof(clkcon));
*clkcon |= (1 << 15);
printk("clkcon = %lx\n", *clkcon);
printk("adc_init ############################\n");
return 0;
err_misc_register_failed:
printk("misc_register failed\n");
misc_deregister(&misc_d);
return ret;
err_request_irq_failed:
printk("request_irq failed\n");
disable_irq(IRQ_ADC);
free_irq(IRQ_ADC, NULL);
misc_deregister(&misc_d);
return ret;
}
static void __exit adc_exit(void)
{
iounmap(clkcon);
iounmap(adcdat0);
iounmap(adccon);
disable_irq(IRQ_ADC);
free_irq(IRQ_ADC, NULL);
misc_deregister(&misc_d);
printk("adc_exit ...\n");
}
module_init(adc2_init);
module_exit(adc_exit);
MODULE_LICENSE("GPL");