1、確定把自己開發代碼放入到內核合適的位置
將demo_chardev.c文件拷貝到.../drivers/char/目錄下。
demo_chardev.c
[cpp] view plain copy
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
/*結構體file_operations定義的頭文件*/
#include <linux/fs.h>
/*聲明copy_to/from_user函數的頭文件*/
#include <linux/uaccess.h>
/*聲明class_create 和device_create相關信息*/
#include <linux/device.h>
#define DEMO_DEBUG
#ifdef DEMO_DEBUG
#define dem_dbg(fmt, arg...) printk(KERN_WARNING fmt, ##arg)
#else
#define dem_dbg(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
#endif
#define DEVICE_COUNT 2
/*記錄當前驅動所占用的主設備號*/
static int major = 0;
static int demo_open (struct inode *pnode, struct file *filp)
{
dem_dbg("[kern func]: %s major: %d minor: %d\n",
__FUNCTION__, imajor(pnode), iminor(pnode));
return 0;
}
static ssize_t demo_read (struct file *filp, char __user *buf, size_t count, loff_t *offp)
{
unsigned char ary[100] = "you are reading successfully!";
unsigned long len = min(count, sizeof(ary)); //min是個宏,用來獲取兩個數中較小的值
int retval;
dem_dbg("[kern func]: %s major: %d minor: %d\n",
__FUNCTION__, imajor(filp->f_dentry->d_inode),
iminor(filp->f_dentry->d_inode));
//file結構體的f_flags成員可用來判斷是否阻塞讀取,然後進行相應處理
if(copy_to_user(buf, ary, len) != 0){
retval = -EFAULT;
goto cp_err;
}
return len; //成功返回實際傳輸的字節數
cp_err:
return retval;
}
static ssize_t demo_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp)
{
unsigned char ary[100] = "";
unsigned long len = min(count, sizeof(ary)); //min是個宏,用來獲取兩個數中較小的值
int retval;
dem_dbg("[kern func]: %s major: %d minor: %d\n",
__FUNCTION__, imajor(filp->f_dentry->d_inode),
iminor(filp->f_dentry->d_inode));
if(copy_from_user(ary, buf, len) != 0){
retval = -EFAULT;
goto cp_err;
}
printk("[msg]: writing context: %s\n",ary);
return len; //成功返回實際傳輸的字節數
cp_err:
return retval;
}
static int demo_release (struct inode *pnode, struct file *filp)
{
dem_dbg("[kern func]: %s major: %d minor: %d\n",
__FUNCTION__, imajor(pnode), iminor(pnode));
return 0;
}
/*@定義file_operations結構體變量*/
static struct file_operations fops = {
.owner = THIS_MODULE,
.read = demo_read,
.write = demo_write,
.open = demo_open,
.release = demo_release,
};
static struct class *demo_class;
static int __init drvdemo_init(void)
{
struct device *demo_device;
int i;
int retval;
dem_dbg("[msg]:this is a driver demo, in module initial function\n");
/*註冊字符驅動函數,成功 返回動態分配好的主設備號,失敗
*返回錯誤碼(負值)*/
major = register_chrdev(0, "demo_chrdev", &fops);
if(major < 0){
retval = major;
goto chrdev_err;
}
/*創建設備類*/
demo_class = class_create(THIS_MODULE,"demo_class");
if(IS_ERR(demo_class)){
retval = PTR_ERR(demo_class);
goto class_err;
}
/*創建設備文件,通知用戶在“/dev/”目錄下創件名字為demoX的設備文件*/
for(i=0; i<DEVICE_COUNT; i++){ //最多可創建255個設備節點(register_chrdev函數會申請0-254範圍的從設備號)
demo_device = device_create(demo_class,NULL, MKDEV(major, i), NULL,"demo%d",i);
if(IS_ERR(demo_device)){
retval = PTR_ERR(demo_device);
goto device_err;
}
}
return 0;
device_err:
while(i--) //設備節點創建的回滾操作 device_destroy(demo_class,MKDEV(major, i));
class_destroy(demo_class); //刪除設備類
class_err:
unregister_chrdev(major, "demo_chrdev");
chrdev_err:
return retval;
}
static void __exit drvdemo_exit(void)
{
int i;
dem_dbg("[msg]:in module exit function\n");
/*註銷字符驅動函數,無返回值,major為已分配的主設備號*/
unregister_chrdev(major, "demo_chrdev");
/*刪除設備節點和設備類*/
for(i=0; i<DEVICE_COUNT; i++)
device_destroy(demo_class,MKDEV(major, i));
class_destroy(demo_class);
}
module_init(drvdemo_init);
module_exit(drvdemo_exit);
MODULE_LICENSE("Dual BSD/GPL"); //BSD/GPL雙重許可證
MODULE_AUTHOR("hanbo"); //模塊作者(可選)
MODULE_DESCRIPTION("used for studing linux drivers"); //模塊兒簡介(可選)
2、把自己開發的功能增加到Linux內核的配置選項中,使用戶能夠選擇此功能
vi drivers/char/Konfig 在文件結尾,endmenu的前面加入壹個config選項
[cpp] view plain copy
config DEMO_CHARDEV
bool "demo_chardev driver for hanbo chardev boards"
default y
help
this is CHARDEV driver for hanbo chardev boards.
3、構建或修改Makefile,根據用戶的選擇,將相應的代碼編譯到最終生成的Linux內核中去
make menuconfig(添加配置選項)(如果提示找不到“ncurses”庫則執行命令: sudo apt-get install libncurses5-dev )
Device driver -->
character devices ->
[*] demo_chardev driver for hanbo chardev boards
4、vi drivers/char/Makefile 添加內容如下:
..........
obj-$(CONFIG_DEMO_CHARDEV) +=demo_chardev.o (添加)
obj-$(CONFIG_JS_RTC) +=js-rtc.o(自帶)
js-rtc-y = rtc.o (自帶)
5、make (更新內核鏡像到開發板)
6、交叉編譯測試程序,放到開發板運行
arm-linux-gcc-gcc test.c -o demo
test.c
[cpp] view plain copy
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char *argv[])
{
int fd1 = 0, fd2 = 0;
unsigned char buf1[100] = "I am a test program!";
unsigned char buf2[100] = {0};
int retval;
//以讀寫、不阻塞方式打開設備文件
fd1 = open("/dev/demo0", O_RDWR | O_NONBLOCK);
if(fd1 < 0){
perror("open /dev/demo1");
goto out;
}
//以只讀、阻塞方式打開設備文件
fd2 = open("/dev/demo1", O_RDONLY);
if(fd2 < 0){
perror("open /dev/demo2");
goto out;
}
//成功返回實際寫入字節數,失敗返回負值
retval = write(fd1, buf1, strlen(buf1)+1);
if(retval < 0){
perror("writing fd1 failed!");
goto out;
}
printf("<user space>: write bytes: %d write content: %s\n", retval, buf1);
//成功返回實際讀取字節數,失敗返回負值
retval = read(fd2, buf2, sizeof(buf2));
if(retval < 0){
perror("reading fd2 failed!");
goto out;
}
printf("<user space>: read bytes: %d read content: %s\n", retval, buf2);
return 0;
out:
if(fd1 > 0)
close(fd1);
if(fd2 > 0)
close(fd2);
return -1;
}
二、手動加載驅動 .ko文件
1、上面的demo_chardev.c文件放到內核下編譯生成 .ko文件
Makefile
[cpp] view plain copy
#如果已定義KERNELRELEASE,說明是由內核構造系統調用的
#可以利用內建語句
ifneq ($(KERNELRELEASE),)
obj-m +=demo_chrdev.o
#此時由內核構造系統調用
else
#定義並記錄內核源碼路徑
KERNELDIR = /home/hanbo/linux-2.6.35.7(自己源碼路徑,2.6.35.7指當前內核版本)
#記錄當前工程目錄
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
@rm -rf *.o .t* .m* .*.cmd *.mod.c *.order *.symvers
endif
clean:
rm -rf *.ko *.o .t* .m* .*.cmd *.mod.c *.order *.symvers
2、 然後用命令加載 .ko 驅動
lsmod 列舉當前系統中的所有模塊
lsmod 列舉當前系統中的所有模塊
rmmod xxx 卸載指定模塊(不需要.ko後綴)
3、如果自己編譯的代碼中沒有用
/*創建設備類*/
demo_class = class_create(THIS_MODULE,"demo_class");
/*創建設備文件,通知用戶在“/dev/”目錄下創件名字為demoX的設備文件*/
demo_device = device_create(demo_class,NULL, MKDEV(major, i), NULL,"demo%d",i);
則需要手動添加設備節點
mknod /dev/demo1 c 主設備號 0
mknod /dev/demo2 c 主設備號 1
註意:若卸載時出現提示 rmmod:chdir(2.6.35.7):No such file or directory
則在開發板根文件系統下創建目錄:/lib/modules/2.6.35.7(跟當前內核版本同名)