linux的驱动插入一定会进过linux内核,为了保证安全性linux内核会直接阻拦应用层直接访问驱动层(也就是在安装linux系统后,裸机的驱动插入方法就无效了),此时需要按照linux系统的插入方法来做,本文为字符设备驱动框架流程:
在这之前先要明白linux内核下的模块怎么摆放的:
让我们从最底下向上出发
1在内核层向linux内核申请插入驱动模块
1)申请设备号(用于代表模块)
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
dev :alloc_chrdev_region函数向内核申请下来的设备号
baseminor :次设备号的起始
count: 申请次设备号的个数
name :执行 cat /proc/devices显示的名称,,表示自己的注释
例如:dev_t devnum;
alloc_chrdev_region(&devnum, 0, 1, "myfifo");
printk("%d %d\n", MAJOR(devnum), MINOR(devnum) );
2)创建一个模块结点(你写的模块)
//功能
struct file_operations myops = {
.open = adc_init,
.read = adc_read,
.unlocked_ioctl = adc_ctl,
.release = adc_release
};
struct cdev *mycdev; //创建一个设备节点
mycdev = cdev_alloc(); //去申请这个节点空间
cdev_init(mycdev, &myops); //将节点与你想要的功能放在一起
上文中的功能放到最下面了
例如:mycdev = cdev_alloc();
cdev_init(mycdev, &myops);
3)绑定模块号与设备节点
int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
这里, dev 是 cdev 结构, num 是这个设备响应的第一个设备号, count 是应当关联到设备的设备号的数目. 常常 count 是 1, 但是有多个设备号对应于一个特定的设备的情形. 例如, 设想 SCSI 磁带驱动, 它允许用户空间来选择操作模式(例如密度), 通过安排多个次编号给每一个物理设备.
cdev_add(MYADC.mycdev, MYADC.devnum, 1);
4)完成,打印插入成功的提示信息
例如:printk("module install-axxxxxxxxxxxxxxxx--------------------------\n");
这样就完成了一个linux中驱动的初始化
具体函数的设置
linux只会给应用层留系统调用接口来直接控制内核,
例如:open,read,write,unlocked——ioctl,release,close,等
通过设置这些方法就可以实现自己想要的系统调用。
具体功能的设置,方法如下
//在相应的系统调用应该进行的函数
struct file_operations myops = {
.open = adc_init,
.read = adc_read,
.unlocked_ioctl = adc_ctl,
.release = adc_release
};
下面来看一个实际adc模块代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <asm/io.h>
#include <asm/ioctl.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
MODULE_AUTHOR("xxx");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("for module test");
#define SETBIT _IO('A', 0)
struct myadc{
struct cdev *mycdev;
dev_t devnum;
int *ADCCON;
int *ADCMUX;
int *ADCDAT;
}MYADC;
//void adc_init(void)
int adc_init(struct inode *i, struct file *f)
{
MYADC.ADCCON = ioremap(0x126c0000, 4);
MYADC.ADCDAT = ioremap(0x126c000c, 4);
MYADC.ADCMUX = ioremap(0x126c001c, 4);
//*MYADC.ADCCON = (*MYADC.ADCCON&~(0xff<<6)) | (200<<6);
iowrite32((ioread32(MYADC.ADCCON)&~(0xff<<6)) | (200<<6) ,MYADC.ADCCON);
//*MYADC.ADCCON |= 1<<14;
iowrite32(ioread32(MYADC.ADCCON)|(1<<14), MYADC.ADCCON);
//*MYADC.ADCCON &= ~(1<<2);
iowrite32((ioread32(MYADC.ADCCON)&~(1<<2)) ,MYADC.ADCCON);
//*MYADC.ADCMUX = 3;
iowrite32(3, MYADC.ADCMUX);
return 0;
}
void adc_set(int count)
{
}
//void adc_release()
int adc_release(struct inode *i, struct file *f)
{
iounmap(MYADC.ADCCON);
iounmap(MYADC.ADCDAT);
iounmap(MYADC.ADCMUX);
return 0;
}
ssize_t adc_read(struct file *f, char __user *dataptr, size_t size, loff_t *offset)
{
int data;
//*MYADC.ADCCON |= 1;//start
iowrite32(ioread32(MYADC.ADCCON)|1, MYADC.ADCCON);
while(!(ioread32(MYADC.ADCCON) & (1<<15)));//procee
data = ioread32(MYADC.ADCDAT)&0xfff;
//printk("xxxxxxxxxx: %d\n", data);
if(copy_to_user(dataptr, &data, sizeof(data))){}
return sizeof(data);
}
//void adc_ctl()
long adc_ctl(struct file *f, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
}
return 0;
}
struct file_operations myops = {
.open = adc_init,
.read = adc_read,
.unlocked_ioctl = adc_ctl,
.release = adc_release
};
static int mymodule_init(void)
{
//1. request dev num
alloc_chrdev_region(&MYADC.devnum, 0, 1, "xxxxxxxxxxxxxxxadcllllllllllllllll");
printk("%d %d\n", MAJOR(MYADC.devnum), MINOR(MYADC.devnum) );
//2. create cdev struct
MYADC.mycdev = cdev_alloc();
cdev_init(MYADC.mycdev, &myops);
//3. insert my cdev into kernel
cdev_add(MYADC.mycdev, MYADC.devnum, 1);
//adc_init();
printk("module install-axxxxxxxxxxxxxxxx--------------------------\n");
return 0;
}
static void mymodule_exit(void)
{
//adc_off();
//1. remove my cdev from kernel
cdev_del(MYADC.mycdev);
//2. free cdev struct
//kfree(mycdev);
//3. free dev num
unregister_chrdev_region(MYADC.devnum, 1);
printk("module release--------------------------------\n");
}
module_init(mymodule_init);
module_exit(mymodule_exit);