【linux操作系統(tǒng)驅動分類】
字符設備: 串口,led,按鍵,一次只讀取一個字節(jié)數(shù)據(jù)
塊設備: 一次讀取多個字節(jié)(512字節(jié)), 硬盤,內(nèi)存
網(wǎng)絡設備: 一次讀取多字節(jié)數(shù)據(jù)
本文引用地址://www.einuk.cn/emb/Column/7464.html
ls -l /dev 查看文件屬性
c開頭的叫字符設備文件(char), 串口,Led,I2C
b開頭的叫塊設備文件 (block)
sudo mknod /dev/led c 500 0 //生成一個設備,跟char-read.ko關聯(lián)
制作一個字符設備
【設備號】
設備和驅動是怎么關聯(lián)到一起的?
是通過500 0 關聯(lián)到一起的
500 主設備號(linux系統(tǒng)將一些常用的硬件設備進行了編號,比如串口主設備號4 ...)
0 次設備號(第一個串口 次設備號0, 1 2.....)
設備號能唯一表示一個設備,所以不可以重復
fs4412_led_drv.c 驅動文件
【如何實現(xiàn)一個字符設備驅動】
1 因為字符設備驅動屬于一個內(nèi)核模塊,所以也使用內(nèi)核模塊模板
2 字符設備驅動使用cdev架構
【cdev中重要的宏】
設備編號:
MKDEV(major, minor);
//功能:構造設備編號
major 主設備號, minor 此設備號
執(zhí)行完此函數(shù),返回一個32位的設備號,其中前12位是主設備號,后20位是次設備號
500 1
00011111 0100 000000000000000000001
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
MAJOR(dev_t);
//功能:取主設備號
從32位的設備號中取出主設備號
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
MINOR(dev_t); //取次設備號
從32位的設備號中取出次設備號
申請設備編號:
int register_chrdev_region(dev_t from, unsigned count, const char *name)
功能:將模塊與主次設備號關聯(lián)
參數(shù):
from,起始設備編號
count,次設備號個數(shù)
name,設備名
返回值:
成功返回0,失敗返回錯誤碼。
釋放設備編號:
void unregister_chrdev_region(dev_t from, unsigned count)
功能:取消模塊與主次設備號關聯(lián)
參數(shù):
from,起始設備編號
count,次設備號個數(shù)
驅動模塊和250 0 設備關聯(lián)
int hello_major = 250;
int hello_minor = 0;
int number_of_devices = 1;
static int __init hello_2_init (void)
{
int result;
dev_t dev = 0;
dev = MKDEV (hello_major, hello_minor);
result = register_chrdev_region (dev, number_of_devices, "hello");
if (result<0) {
printk (KERN_WARNING "hello: can't get major number %d\n", hello_major);
return result;
}
printk (KERN_INFO "Registered character driver\n");
return 0;
}
static void __exit hello_2_exit (void)
{
dev_t devno = MKDEV (hello_major, hello_minor);
unregister_chrdev_region (devno, number_of_devices);
printk (KERN_INFO "Char driver cleaned up\n");
}
module_init (hello_2_init);
module_exit (hello_2_exit);
sudo insmod hello.ko
cat /proc/devices | grep hello //linux會將注冊好的設備添加進/proc/devices文件中
sudo rmmod hello
struct cdev{
const struct file_operations *ops; //字符設備驅動結構體
dev_t dev; //主次設備號
unsigned int count; //設備數(shù)量
};
初始化字符設備:
void cdev_init(struct cdev *cdev, const struct file_operations *fops);
添加設備:
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
刪除設備:
void cdev_del(struct cdev *p)
下面的代碼是完成一個簡單的字符設備驅動程序,此程序沒有實際功能,知識完成了open, close函數(shù)
//定義主設備號,次設備號
#include
#include
#include
#include
#include
#include
int hello_major = 250; //主設備號
int hello_minor = 0; //次設備號
struct cdev cdev; //c -> char dev->device 字符設備的主要結構體
static int hello_open (struct inode *inode, struct file *file) //應用成執(zhí)行open函數(shù)會調(diào)用到它
{
printk ("11111111111111111111111111 device opened\n");
return 0;
}
struct file_operations hello_fops = { //將hello_open hello_close 與 open , close 關聯(lián)
.owner = THIS_MODULE,
.open = hello_open,
};
static void char_reg_setup_cdev (void) //完成cdev結構體的初始化
{
dev_t devno;
devno = MKDEV (hello_major, hello_minor); //MKDEV宏能將主次設備號放在一個32位的變量中,變成一個編號
cdev_init (&cdev, &hello_fops); //將hello_fops 放到cdev結構體中
cdev.owner = THIS_MODULE;
cdev_add (&cdev, devno , 1); //將設備號再放到cdev結構體中, 1代表 子設備數(shù)量1個
}
static int __init hello_init (void) //注冊模塊時要執(zhí)行此函數(shù)
{
int result;
dev_t devno;
devno = MKDEV (hello_major, hello_minor);
result = register_chrdev_region (devno, 1, "hello");
char_reg_setup_cdev ();
printk ("char device registered\n");
return 0;
}
static void __exit hello_exit (void)
{
dev_t devno = MKDEV (hello_major, hello_minor);
cdev_del (&cdev); //刪除cdev
unregister_chrdev_region (devno, 1); //刪除250, 0號設備
}
module_init (hello_init);
module_exit (hello_exit);
完成一個應用程序,測試驅動
#include
#include
#include
#include
#include
#include
#include
int main()
{
int fd;
fd = open("/dev/hello", O_RDWR);
if(fd > 0)
{
printf("file open success\n");
}
else
{
printf("file open failed\n");
}
}
制作字符設備的過程
1 制作驅動模塊
1)將驅動代碼 寫到hello.c中
2)make (會生成hello.ko文件)
2 sudo insmod hello.ko
3 sudo mknod /dev/hello c 250 0 //生成一個設備,跟char-read.ko關聯(lián)
4 gcc -o test test.c //將應用層的test.c 編譯生成test可執(zhí)行文件
5 sudo ./test
6 dmesg