1用戶空間無傳值給內核空間
模塊文件位於: /work/xyc_drivers_and_test
模塊源文件:first_drv.c
模塊文件:first_drv.ko
測試文件: firsttest.c
1.1 手工在設備上創建設備文件(即在設備上創建設備文件是手工輸入mknod命令的)
1.1.1 手工分配設備號
1.1.1.1 編寫驅動源文件first_drv.c
Ⅰ.編輯open, write對應的驅動程序first_drv_open,first_drv_write,first_drv_open ,first_drv_write只是打印一些信息
static int first_drv_open(struct inode *inode, struct file *file)
{
printk("first_drv_open\n");
return 0;
}
static int first_drv_write(struct file *file, const char *data, size_t len, loff_t *ppos)
{
printk("first_drv_write\n");
return 0;
}
Ⅱ.定義並填充file_operations 結構體first_drv_fops:
static const struct file_operations first_drv_fops = {
.owner = THIS_MODULE,
.write = first_drv_write,
.open = first_drv_open,
};
Ⅲ.file_operations 結構體first_drv_fops掛載到鏈表中,鏈表索引爲111(即主設備號爲111),設備名填充爲xxx
用register_chrdev()字符設備註冊函數將主設備號和file_operations 結構體first_drv_fops聯繫起來,並在驅動入口(初始化)函數first_drv_init()調用register_chrdev()字符註冊函數
同理,用unregister_chrdev()字符設備卸載函數將主設備號和file_operations 結構體first_drv_fops分離,並釋放設備號,並在驅動入口(初始化)函數first_drv_exit()調用unregister_chrdev()字符卸載註冊函數
/*
* 執行insmod命令時就會調用這個函數
*/
static int __init first_drv_init(void)
{
int ret;
/* 註冊字符設備
* 參數爲主設備號、設備名字、file_operations結構;
* 這樣,主設備號就和具體的file_operations結構聯繫起來了,
* 操作主設備爲LED_MAJOR的設備文件時,就會調用s3c24xx_leds_fops中的相關成員函數
* LED_MAJOR可以設爲0,表示由內核自動分配主設備號
*/
// ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c24xx_leds_fops);
ret = register_chrdev(111, "xxx", &first_drv_fops);// /* 加載模式後,執行”cat /proc/devices”命令看到的設備名稱 */
if (ret < 0) {
printk( " can't register major number\n");
return ret;
}
printk( " initialized\n");
return 0;
}
/*
* 執行rmmod命令時就會調用這個函數
*/
static void __exit first_drv_exit(void)
{
/* 卸載驅動程序 */
unregister_chrdev(111, "xxx");
}
insmod first_drv.ko後,cat /proc/devices可以看到 111 xxx這一字符設備,111主設備號,xxx爲設備名,是測試前cat /proc/devices
沒被佔用的主設備號Ⅳ.告知first_drv.ko模塊其入口和出口函數
insmod first_drv.ko/rmmod first_drv.ko 加載/卸載驅動模塊時還不知道first_drv.ko的入口函數和出口函數,因此需要告知內核,first_drv_init(),first_drv_exit()爲first_drv.ko的入口和出口函數,這樣insmod first_drv.ko時就會調用first_drv_init()執行驅動模塊的初始化,rmmod first_drv.ko調用first_drv_exit()執行驅動模塊的卸載
/* 這兩行指定驅動程序的初始化函數和卸載函數 */
module_init(first_drv_init);
module_exit(first_drv_exit);
Ⅴ.加上所有函數的頭文件:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
當應用程序open/write /dev/xyz時,通過主設備號,次設備號/設備種類(c字符設備)找到鏈表項,得到first_drv_open()函數和first_drv_write()函數的地址,調用之
合起來Ⅰ,Ⅱ,Ⅲ,Ⅳ,Ⅴ的驅動源文件first_drv.c:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
static int first_drv_open(struct inode *inode, struct file *file)
{
printk("first_drv_open\n");
return 0;
}
static int first_drv_write(struct file *file, const char *data, size_t len, loff_t *ppos)
{
printk("first_drv_write\n");
return 0;
}
static const struct file_operations first_drv_fops = {
.owner = THIS_MODULE,
.write = first_drv_write,
.open = first_drv_open,
};
/*
* 執行insmod命令時就會調用這個函數
*/
static int __init first_drv_init(void)
{
int ret;
/* 註冊字符設備
* 參數爲主設備號、設備名字、file_operations結構;
* 這樣,主設備號就和具體的file_operations結構聯繫起來了,
* 操作主設備爲LED_MAJOR的設備文件時,就會調用s3c24xx_leds_fops中的相關成員函數
* LED_MAJOR可以設爲0,表示由內核自動分配主設備號
*/
// ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c24xx_leds_fops);
ret = register_chrdev(111, "xxx", &first_drv_fops);// /* 加載模式後,執行”cat /proc/devices”命令看到的設備名稱 */
if (ret < 0) {
printk( " can't register major number\n");
return ret;
}
printk( " initialized\n");
return 0;
}
/*
* 執行rmmod命令時就會調用這個函數
*/
static void __exit first_drv_exit(void)
{
/* 卸載驅動程序 */
unregister_chrdev(111, "xxx");
}
/* 這兩行指定驅動程序的初始化函數和卸載函數 */
module_init(first_drv_init);
module_exit(first_drv_exit);
1.1.1.2 編寫驅動模塊Makefile:
KERN_DIR = /work/system/linux-2.6.22.6
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += first_drv.o
KERN_DIR是內核存放的位置,這個內核和開發板flash啓動的內核是同一內核,如果此內核沒有製作,需要先製作:
1.1.1.3 編寫測試文件firsttest.c:
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
4 #include <unistd.h>
5
6 int main()
7 {
8 int fd1;
9 fd1 = open("/dev/xyz", O_RDWR);
10 if(fd1<0)
11 printf("open failed\n");
12 write(fd1, "aaa", 4);
13 return 0;
14 }
其中/dev/xyz爲設備文件名,register_chrdev(111, "xxx", &first_drv_fops)中的"xxx"爲設備名,編譯測試文件firsttest.c
1.1.1.4 測試驅動模塊:
Character devices:
1 mem
2 pty
3 ttyp
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
6 lp
7 vcs
10 misc
13 input
14 sound
29 fb
90 mtd
99 ppdev
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
204 s3c2410_serial
253 usb_endpoint
254 rtc
Block devices:
1 ramdisk
7 loop
8 sd
31 mtdblock
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
initialized
first_drv 1824 0
1 mem
2 pty
3 ttyp
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
6 lp
7 vcs
10 misc
13 input
14 sound
29 fb
90 mtd
99 ppdev
111 xxx
first_drv_write
1.1.2 自動分配設備號
/*
* 執行insmod命令時就會調用這個函數
*/
int major;
static int __init first_drv_init(void)
{
/* 註冊字符設備
* 參數爲主設備號、設備名字、file_operations結構;
* 這樣,主設備號就和具體的file_operations結構聯繫起來了,
* 操作主設備爲LED_MAJOR的設備文件時,就會調用s3c24xx_leds_fops中的相關成員函數
* LED_MAJOR可以設爲0,表示由內核自動分配主設備號
*/
// ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c24xx_leds_fops);
major= register_chrdev(0, "xxx", &first_drv_fops);// /* 加載模式後,執行”cat /proc/devices”命令看到的設備名稱 */
if (major < 0) {
printk( " can't register major number\n");
return major;
}
printk( " initialized\n");
return 0;
}
/*
* 執行rmmod命令時就會調用這個函數
*/
static void __exit first_drv_exit(void)
{
/* 卸載驅動程序 */
unregister_chrdev(major, "xxx");
}
unregister_chrdev(111, "xxx")改爲unregister_chrdev(major, "xxx") 不能忘記,否則cat /proc/devices還存在111 xxx的字符設備Character devices:
1 mem
2 pty
3 ttyp
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
6 lp
7 vcs
10 misc
13 input
14 sound
29 fb
90 mtd
99 ppdev
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
204 s3c2410_serial
252 xxx
可知xxx的主設備號爲252#rm /dev/xyz //刪除設備文件/dev/xyz
first_drv_write
1.2 自動在設備上創建設備文件
static struct class *firstdrv_class;
static struct class_device *firstdrv_class_dev;
2.在入口函數中填充類和該類的設備(first_drv_fops註冊到major後)
firstdrv_class = class_create(THIS_MODULE, "first_drv");
firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz");
3.在出口函數中卸載該類以及類下的設備(first_drv_fops和major卸載後)
class_device_unregister(firstdrv_class_dev);
class_destroy(firstdrv_class);
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
static struct class *firstdrv_class;
static struct class_device *firstdrv_class_dev;
static int first_drv_open(struct inode *inode, struct file *file)
{
printk("first_drv_open\n");
return 0;
}
static int first_drv_write(struct file *file, const char *data, size_t len, loff_t *ppos)
{
printk("first_drv_write\n");
return 0;
}
static const struct file_operations first_drv_fops = {
.owner = THIS_MODULE,
.write = first_drv_write,
.open = first_drv_open,
};
/*
* 執行insmod命令時就會調用這個函數
*/
int major;
static int __init first_drv_init(void)
{
/* 註冊字符設備
* 參數爲主設備號、設備名字、file_operations結構;
* 這樣,主設備號就和具體的file_operations結構聯繫起來了,
* 操作主設備爲LED_MAJOR的設備文件時,就會調用s3c24xx_leds_fops中的相關成員函數
* LED_MAJOR可以設爲0,表示由內核自動分配主設備號
*/
// ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c24xx_leds_fops);
major= register_chrdev(0, "xxx", &first_drv_fops);// /* 加載模式後,執行”cat /proc/devices”命令看到的設備名稱 */
if (major < 0) {
printk( " can't register major number\n");
return major;
}
printk( " initialized\n");
firstdrv_class = class_create(THIS_MODULE, "first_drv");
firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz");
return 0;
}
/*
* 執行rmmod命令時就會調用這個函數
*/
static void __exit first_drv_exit(void)
{
/* 卸載驅動程序 */
unregister_chrdev(major, "xxx");
class_device_unregister(firstdrv_class_dev);
class_destroy(firstdrv_class);
}
/* 這兩行指定驅動程序的初始化函數和卸載函數 */
module_init(first_drv_init);
module_exit(first_drv_exit);
測試驅動模塊:first_drv: Unknown symbol class_device_unregister
first_drv: Unknown symbol class_create
first_drv: Unknown symbol class_destroy
insmod: cannot insert 'first_drv.ko': Unknown symbol in module (-1): No such file or directory
crw-rw---- 1 0 0 251, 0 Jan 1 00:44 /dev/xyz //可知在MKDEV(251, 0)字符設備上自動創建了設備文件/dev/xyz
Character devices:
1 mem
2 pty
3 ttyp
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
6 lp
7 vcs
10 misc
13 input
14 sound
29 fb
90 mtd
99 ppdev
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
204 s3c2410_serial
251 xxx
<pre name="code" class="cpp">#
#./firsttest
first_drv_open
first_drv_write
# cd sys/class/
# ls
first_drv mem ppdev scsi_host usb_host
graphics misc printer sound vc
hwmon mmc_host rtc spi_master vtconsole
i2c-adapter mtd scsi_device tty
input net scsi_disk usb_endpoint
# cd first_drv/
# ls
xyz
# ls -l xyz/
-r--r--r-- 1 0 0 4096 Jan 1 00:44 dev
lrwxrwxrwx 1 0 0 0 Jan 1 00:46 subsystem -> ../../../class/first_drv
--w------- 1 0 0 4096 Jan 1 00:46 uevent
# cd xyz/
# vi dev
251:0
2 用戶空間有傳值給內核空間
2.1 同時點亮或熄滅3個LED
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <asm-arm/io.h>
#include <asm-arm/uaccess.h>
#define DEVICE_NAME "leds"
int leds_major;
static struct class *leds_class;
static struct class_device *leds_class_dev;
static volatile unsigned long *gpfcon = NULL;
static volatile unsigned long *gpfdat =NULL;
/* 應用程序對設備文件/dev/leds執行open(...)時,
* 就會調用s3c24xx_leds_open函數
*/
static int s3c24xx_leds_open(struct inode *inode, struct file *file)
{
/*配置GPF4,GPF5,GPF6爲輸出引腳*/
*gpfcon &= (~(0x3<<(4*2))) &(~(0x3<<(5*2)) ) & ( ~(0x3<<(6*2)) ) ;
*gpfcon |=(0x1<<(4*2) ) | (0x1<<(5*2)) |(0x1<<(6*2));
return 0;
}
static int s3c24xx_leds_write(struct file *file, const char *data, size_t len, loff_t *ppos)
{
char value;
copy_from_user(&value, data, 1);
if( value ){
/*亮燈*/
*gpfdat &= (~(0x1<<4)) & (~(0x1<<5)) & (~(0x1<<6)) ;
}
else{
/*滅燈*/
*gpfdat |=(0x1<<4) |(0x1<<5) | (0x1<<6) ;
}
return 0;
}
static struct file_operations s3c24xx_leds_fops = {
.owner = THIS_MODULE, /* 這是一個宏,推向編譯模塊時自動創建的__this_module變量 */
.open = s3c24xx_leds_open,
.write = s3c24xx_leds_write,
};
static int __init s3c24xx_leds_init(void)
{
leds_major = register_chrdev(0, DEVICE_NAME, &s3c24xx_leds_fops);
if (leds_major < 0) {
printk( " can't register major number\n");
return leds_major;
}
printk( " initialized\n");
leds_class = class_create(THIS_MODULE, "leds");
leds_class_dev = class_device_create(leds_class, NULL, MKDEV(leds_major, 0), NULL, "leds");
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
gpfdat = gpfcon +1;
return 0;
}
static void __exit s3c24xx_leds_exit(void)
{
unregister_chrdev(leds_major, DEVICE_NAME);
class_device_unregister(leds_class_dev);
class_destroy(leds_class);
iounmap(gpfcon);
}
module_init(s3c24xx_leds_init);
module_exit(s3c24xx_leds_exit);
MODULE_LICENSE("GPL");
比上一節多了:
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
gpfdat = gpfcon +1;
iounmap(gpfcon);
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd1;
char val;
fd1 = open("/dev/leds", O_RDWR);
if(fd1<0)
printf("open failed\n");
if(argc !=2){
printf("Usage:\n");
printf("%s <on|off>\n", argv[0]);
return -1;
}
if(strcmp(argv[1], "on") == 0){
val =1;
write(fd1, &val, 1);
}
else if(strcmp(argv[1], "off") == 0){
val =0;
write(fd1, &val, 1);
}
else {
printf("Usage:\n");
printf("%s <on|off>\n", argv[0]);
return -1;
}
return 0;
}
將測試代碼和驅動代碼分別編譯後拷貝到網絡根文件系統目錄下:
./ledstest <on|off>
./ledstest <on|off>
2.2 同時點亮/熄滅 ,分別點亮/熄滅
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <asm-arm/io.h>
#include <asm-arm/uaccess.h>
#define DEVICE_NAME "leds"
static int leds_major;
static struct class *leds_class;
static struct class_device *leds_class_dev[4];
static volatile unsigned long *gpfcon = NULL;
static volatile unsigned long *gpfdat =NULL;
/* 應用程序對設備文件/dev/leds執行open(...)時,
* 就會調用s3c24xx_leds_open函數
*/
static int s3c24xx_leds_open(struct inode *inode, struct file *file)
{
/*配置GPF4,GPF5,GPF6爲輸出引腳*/
*gpfcon &= (~(0x3<<(4*2))) &(~(0x3<<(5*2)) ) & ( ~(0x3<<(6*2)) ) ;
*gpfcon |=(0x1<<(4*2) ) | (0x1<<(5*2)) |(0x1<<(6*2));
return 0;
}
static int s3c24xx_leds_write(struct file *file, const char *data, size_t len, loff_t *ppos)
{
char value;
int minor = MINOR(file->f_dentry->d_inode->i_rdev);
copy_from_user(&value, data, 1);
#if 0
if( value ){
/*亮燈*/
*gpfdat &= (~(0x1<<4)) & (~(0x1<<5)) & (~(0x1<<6)) ;
}
else{
/*滅燈*/
*gpfdat |=(0x1<<4) |(0x1<<5) | (0x1<<6) ;
}
#endif
switch(minor)
{
case 0:
{
if( value ){
/*全亮燈*/
*gpfdat &= (~(0x1<<4)) & (~(0x1<<5)) & (~(0x1<<6)) ;
}
else{
/*全滅燈*/
*gpfdat |=(0x1<<4) |(0x1<<5) | (0x1<<6) ;
}
break;
}
case 1:
{
if( value ){
/*LED4 亮燈*/
*gpfdat &= (~(0x1<<4));
}
else{
/*LED4 滅燈*/
*gpfdat |=(0x1<<4) ;
}
break;
}
case 2:
{
if( value ){
/*LED5 亮燈*/
*gpfdat &= (~(0x1<<5)) ;
}
else{
/*LED5 滅燈*/
*gpfdat |=(0x1<<5);
}
break;
}
case 3:
{
if( value ){
/*LED6 亮燈*/
*gpfdat &=(~(0x1<<6)) ;
}
else{
/*LED6 滅燈*/
*gpfdat |= (0x1<<6) ;
}
break;
}
default:
break;
}
return 0;
}
static struct file_operations s3c24xx_leds_fops = {
.owner = THIS_MODULE, /* 這是一個宏,推向編譯模塊時自動創建的__this_module變量 */
.open = s3c24xx_leds_open,
.write = s3c24xx_leds_write,
};
/*
*4個設備文件澹:/dev/leds 控制LED4 LED5 LED6
* /dev/leds1 控制LED4
* /dev/leds2 控制LED5
* /dev/leds3 控制LED6
*/
static int __init s3c24xx_leds_init(void)
{
int minor = 0;
leds_major = register_chrdev(0, DEVICE_NAME, &s3c24xx_leds_fops);
if (leds_major < 0) {
printk( " can't register major number\n");
return leds_major;
}
printk( " initialized\n");
leds_class = class_create(THIS_MODULE, "leds");
leds_class_dev[0] = class_device_create(leds_class, NULL, MKDEV(leds_major, minor), NULL, "leds");
for( minor=1; minor<4; minor++){
leds_class_dev[minor] = class_device_create(leds_class, NULL, MKDEV(leds_major, minor), NULL, "leds%d", minor);
}
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
gpfdat = gpfcon +1;
return 0;
}
static void __exit s3c24xx_leds_exit(void)
{
int minor;
unregister_chrdev(leds_major, DEVICE_NAME);
for(minor=0; minor<4; minor++){
class_device_unregister(leds_class_dev[minor]);
}
class_destroy(leds_class);
iounmap(gpfcon);
}
module_init(s3c24xx_leds_init);
module_exit(s3c24xx_leds_exit);
MODULE_LICENS
測試源碼ledstest.c:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
void print_Usage(char *file)
{
printf("Usage:\n");
printf("%s <dev> <on|off>\n", file);
printf("eg.\n");
printf("%s /dev/leds <on|off>\n", file);
printf("%s /dev/leds1 <on|off>\n", file);
printf("%s /dev/leds2 <on|off>\n", file);
printf("%s /dev/leds3 <on|off>\n", file);
}
int main(int argc, char *argv[])
{
int fd1;
char val;
if(argc !=3){
print_Usage(argv[0]);
return -1;
}
fd1 = open(argv[1], O_RDWR);
if(fd1<0)
printf("open %s failed\n", argv[1]);
if(!strcmp(argv[2], "on")){
val =1;
write(fd1, &val, 1);
}
else if(strcmp(argv[2], "off") == 0){
val =0;
write(fd1, &val, 1);
}
else {
print_Usage(argv[0]);
return -1;
}
return 0;
}
將測試代碼和驅動代碼分別編譯後拷貝到網絡根文件系統目錄下:
# insmod leds.ko
initialized
# ./ledstest /dev/leds on
# ./ledstest /dev/leds off
# ./ledstest /dev/leds1 on
# ./ledstest /dev/leds1 off
# ./ledstest /dev/leds on
# ./ledstest /dev/leds off
# ./ledstest /dev/leds1 on
# ./ledstest /dev/leds1 off
# ./ledstest /dev/leds2 on
# ./ledstest /dev/leds2 off
# ./ledstest /dev/leds2 on
# ./ledstest /dev/leds3 on
# ./ledstest /dev/leds1 on
# ./ledstest /dev/leds on
# ./ledstest /dev/leds3 off
# ./ledstest /dev/leds2 off
# ./ledstest /dev/leds3 on
# ./ledstest /dev/ on
open /dev/ failed
# ./ledstest /dev/leds on
# ./ledstest /dev/leds off
# ./ledstest /dev/leds1 on
# ./ledstest /dev/leds1 off
# ./ledstest /dev/leds2 on
# ./ledstest /dev/leds1 on
# ./ledstest /dev/leds2 off
# ./ledstest /dev/leds3 on
# ./ledstest /dev/leds2 off
# ./ledstest /dev/leds2 on
# ./ledstest /dev/leds1 off
# ./ledstest /dev/leds off
# ./ledstest /dev/leds oft
Usage:
./ledstest <dev> <on|off>
eg.
./ledstest /dev/leds <on|off>
./ledstest /dev/leds1 <on|off>
./ledstest /dev/leds2 <on|off>
./ledstest /dev/leds3 <on|off>
#
測試看相應的結果: