(4) vi first_drv.c(自动分配主设备号)
int major;
static int first_drv_init(void)
{
major = register_chrdev(0,"first_drv",&first_drv_fops);
printk("first_drv_init\n");
return 0;
}
static void first_drv_exit(void)
{
unregister_chrdev(major,"first_drv");
printk("first_drv_exit\n");
}
make编译通过。insmod后查看自动分配的设备号:
# insmod first_drv.ko
first_drv_init
/mnt/3 # cat /proc/devices
Character devices:
248 first_drv
测试驱动:# ./firstdrvtest
can't open /dev/xxx !
当然通不过了,主设备号不是111了。
# ls -l /dev/xxx
crw-r--r-- 1 0 0 111, 0 Jan 3 05:35 /dev/xxx
重新新建设备文件,测试成功。
# rm /dev/xxx
/mnt/3 # mknod /dev/xxx c 248 0
/mnt/3 # ./firstdrvtest
first_drv_open
(5) vi first_drv.c(mdev子系统自动创建设备文件)
int major;
static struct class *firstdrv_class;
static struct class_device *firstdrv_device;
static int first_drv_init(void)
{
major = register_chrdev(0,"first_drv",&first_drv_fops);
firstdrv_class = class_create(THIS_MODULE,"firstdrv");
firstdrv_device = class_device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"xxx");
printk("first_drv_init\n");
return 0;
}
static void first_drv_exit(void)
{
unregister_chrdev(major,"first_drv");
class_device_unregister(firstdrv_device);
class_destroy(firstdrv_class);
printk("first_drv_exit\n");
}
make出现错误:
error: implicit declaration of function 'class_create' [-Werror=implicit-function-declaration]
error: implicit declaration of function 'class_device_create' [-Werror=implicit-function-declaration]
增加头文件#include <linux/device.h> 后,依然报错:
error: implicit declaration of function 'class_device_create' [-Werror=implicit-function-declaration]
要使用:
static struct device *firstdrv_device;
firstdrv_device = device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"xxx");
device_destroy(firstdrv_class,MKDEV(major,0));
编译通过。
测试驱动:# cat /dev/xxx
cat: can't open '/dev/xxx': No such file or directory
# insmod first_drv.ko
first_drv_init
# cat /sys/class/firstdrv/xxx/dev
248:0
# cat /dev/xxx
first_drv_open
最后代码:
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
static int first_drv_open(struct inode *inode,struct file *file)
{
printk("first_drv_open\n");
return 0;
}
static ssize_t first_drv_write(struct file *file,const char __user *buf,size_t len,loff_t *ppos)
{
printk("first_drv_write\n");
return 0;
}
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE,
.open = first_drv_open,
.write = first_drv_write,
};
int major;
static struct class *firstdrv_class;
static struct device *firstdrv_device;
static int first_drv_init(void)
{
major = register_chrdev(0,"first_drv",&first_drv_fops);
firstdrv_class = class_create(THIS_MODULE,"firstdrv");
firstdrv_device = device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"xxx");
printk("first_drv_init\n");
return 0;
}
static void first_drv_exit(void)
{
unregister_chrdev(major,"first_drv");
device_destroy(firstdrv_class,MKDEV(major,0));
class_destroy(firstdrv_class);
printk("first_drv_exit\n");
}
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");
(6) vi first_drv.c(点灭全部灯)
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;
static int first_drv_open(struct inode *inode,struct file *file)
{
gpfcon &= ~(0x3<<(4*2)|(0x3<<(5*2))|(0x3<<(6*2)));
gpfcon |= (0x1<<(4*2)|(0x1<<(5*2))|(0x1<<(6*2)));
return 0;
}
static ssize_t first_drv_write(struct file *file,const char __user *buf,size_t len,loff_t *ppos)
{
int val;
copy_from_user(&val,buf,len);
if(val==1)
gpfdat &= ~((1<<4)|(1<<5)|(1<<6));
else
gpfdat |= ((1<<4)|(1<<5)|(1<<6));
return 0;
}
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE,
.open = first_drv_open,
.write = first_drv_write,
};
int major;
static struct class *firstdrv_class;
static struct device *firstdrv_device;
static int first_drv_init(void)
{
major = register_chrdev(0,"first_drv",&first_drv_fops);
firstdrv_class = class_create(THIS_MODULE,"firstdrv");
firstdrv_device = device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"xxx");
gpfcon = (volatile unsigned long *)ioremap(0x56000050,16);
gpfdat = gpfcon + 1;
printk("first_drv_init\n");
return 0;
}
static void first_drv_exit(void)
{
iounmap(gpfcon);
unregister_chrdev(major,"first_drv");
device_destroy(firstdrv_class,MKDEV(major,0));
class_destroy(firstdrv_class);
printk("first_drv_exit\n");
}
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");
make出现invalid operands to binary & (have 'volatile long unsigned int *' and 'int')
原来是指针操作错误,加解引用符号后为:*gpfcon &= ~(0x3<<(4*2)|(0x3<<(5*2))|(0x3<<(6*2)));
*gpfcon |= (0x1<<(4*2)|(0x1<<(5*2))|(0x1<<(6*2)));*gpfdat &= ~((1<<4)|(1<<5)|(1<<6));
*gpfdat |= ((1<<4)|(1<<5)|(1<<6));
make出现error: implicit declaration of function 'copy_from_user' [-Werror=implicit-function-declaration],则加头文件#include <linux/uaccess.h>。
make出现error: implicit declaration of function 'ioremap' [-Werror=implicit-function-declaration],则加头文件#include <asm/mach/map.h>,编译成功。
vi firstdrvtest.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[])
{
int fd;
int val;
fd = open("/dev/xxx",O_RDWR);
if(fd<0)
printf("can't open /dev/xxx !\n");
if(argc!=2)
{
printf("Usage: \n");
printf("%s <on|off>\n",argv[0]);
return 0;
}
if(strcmp(argv[1],"on")==0)
val = 1;
else
val = 0;
write(fd,&val,4);
return 0;
}
编译出现warning: implicit declaration of function 'strcmp' [-Wimplicit-function-declaration],则man 3 strcmp,得到并加头文件 #include <string.h>
编译出现warning: implicit declaration of function 'write' [-Wimplicit-function-declaration],则man 2 write,得到并加头文件#include <unistd.h>,编译通过。
加载测试驱动:insmod first_drv.ko
first_drv_init
# ./firstdrvtest
Usage:
./firstdrvtest <on|off>
# ./firstdrvtest on //3盏灯同时点亮
# ./firstdrvtest off //3盏灯同时熄灭
(7) vi first_drv.c(点灭一灯)
static struct device firstdrv_device[4];
static int first_drv_init(void)
{
int i;
major = register_chrdev(0,"first_drv",&first_drv_fops);
firstdrv_class = class_create(THIS_MODULE,"firstdrv");
firstdrv_device[0] = device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"leds");
for(i=1;i<4;i++)
{
firstdrv_device[i] = device_create(firstdrv_class,NULL,MKDEV(major,i),NULL,"led%d",i);
}
gpfcon = (volatile unsigned long *)ioremap(0x56000050,16);
gpfdat = gpfcon + 1;
printk("first_drv_init\n");
return 0;
}
static ssize_t first_drv_write(struct file *file,const char __user *buf,size_t len,loff_t *ppos)
{
char val;
int minor = MINOR(file->f_dentry->d_inode->i_rdev);
copy_from_user(&val,buf,len);
switch(minor)
{
case 0:
if(val==1)
*gpfdat &= ~((1<<4)|(1<<5)|(1<<6));
else
*gpfdat |= ((1<<4)|(1<<5)|(1<<6));
break;
case 1:
if(val==1)
*gpfdat &= ~((1<<4));
else
*gpfdat |= ((1<<4));
break;
case 2:
if(val==1)
*gpfdat &= ~((1<<5));
else
*gpfdat |= ((1<<5));
break;
case 3:
if(val==1)
*gpfdat &= ~((1<<6));
else
*gpfdat |= ((1<<6));
break;
}
return 0;
}
make出现 error: 'struct file' has no member named 'f_dentry',原来是代码太老了,参考编译驱动时error: ‘struct file’ has no member named ‘f_dentry’-CSDN博客 将MINOR(file->f_dentry->d_inode->i_rdev)改成MINOR(file_inode(file)->i_rdev)。
再次make出现error: incompatible types when assigning to type 'struct device' from type 'struct device *',疏忽了,应该是指针数组static struct device *firstdrv_device[4]; 改完编译通过。
vi firstdrvtest.c
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[])
{
int fd;
char val;
char *filename;
if(argc!=3)
{
printf("Usage: \n");
printf("%s </dev/led?> <on|off>\n",argv[0]);
return 0;
}
filename = argv[1];
fd = open(filename,O_RDWR);
if(fd<0)
{
printf("can't open %s !\n",filename);
return 0;
}
if(strcmp(argv[2],"on")==0)
val = 1;
else if(strcmp(argv[2],"off")==0)
val = 0;
else
{
printf("Usage: \n");
printf("%s </dev/led?> <on|off>\n",argv[0]);
return 0;
}
write(fd,&val,4);
return 0;
}
加载测试驱动:# insmod first_drv.ko
first_drv_init
# ls /dev/led* -l
crw-rw---- 1 0 0 248, 1 Jan 4 00:07 /dev/led1
crw-rw---- 1 0 0 248, 2 Jan 4 00:07 /dev/led2
crw-rw---- 1 0 0 248, 3 Jan 4 00:07 /dev/led3
crw-rw---- 1 0 0 248, 0 Jan 4 00:07 /dev/leds
# ./firstdrvtest
Usage:
./firstdrvtest </dev/led?> <on|off>
# ./firstdrvtest /dev/led1 on
------------[ cut here ]------------
WARNING: CPU: 0 PID: 1057 at ./include/linux/thread_info.h:134 first_drv_write+0x15c/0x190 [first_drv]
Buffer overflow detected (1 < 4)!
Modules linked in: first_drv(O) [last unloaded: first_drv]
CPU: 0 PID: 1057 Comm: firstdrvtest Tainted: G O 4.19.0-rc3 #6
Hardware name: SMDK2440
[<c0010698>] (unwind_backtrace) from [<c000dccc>] (show_stack+0x10/0x18)
[<c000dccc>] (show_stack) from [<c04e8f2c>] (dump_stack+0x18/0x24)
[<c04e8f2c>] (dump_stack) from [<c0019a8c>] (__warn+0xe8/0x110)
[<c0019a8c>] (__warn) from [<c0019af8>] (warn_slowpath_fmt+0x44/0x6c)
[<c0019af8>] (warn_slowpath_fmt) from [<bf03818c>] (first_drv_write+0x15c/0x190 [first_drv])
[<bf03818c>] (first_drv_write [first_drv]) from [<c00c34c8>] (__vfs_write+0x2c/0x174)
[<c00c34c8>] (__vfs_write) from [<c00c37b4>] (vfs_write+0xa0/0x188)
[<c00c37b4>] (vfs_write) from [<c00c3a04>] (ksys_write+0x4c/0xb8)
[<c00c3a04>] (ksys_write) from [<c0009000>] (ret_fast_syscall+0x0/0x50)
Exception stack(0xc3187fa8 to 0xc3187ff0)
7fa0: 00010654 00000000 00000003 beebfc47 00000004 00000000
7fc0: 00010654 00000000 000103e8 00000004 00000000 00000000 b6f81000 beebfc54
7fe0: 00000070 beebfc20 0001062c b6ec8848
---[ end trace 8864fd30a4029042 ]---
?????????????????吓我一跳,原来是write(fd,&val,4);忘了改成write(fd,&val,1);
修改后再试,成功点亮点灭: # ./firstdrvtest /dev/led1 on
# ./firstdrvtest /dev/led1 off
最后驱动代码:
#include <asm/mach/map.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;
static int first_drv_open(struct inode *inode,struct file *file)
{
*gpfcon &= ~(0x3<<(4*2)|(0x3<<(5*2))|(0x3<<(6*2)));
*gpfcon |= (0x1<<(4*2)|(0x1<<(5*2))|(0x1<<(6*2)));
return 0;
}
static ssize_t first_drv_write(struct file *file,const char __user *buf,size_t len,loff_t *ppos)
{
char val;
int minor = MINOR(file_inode(file)->i_rdev);
copy_from_user(&val,buf,len);
switch(minor)
{
case 0:
if(val==1)
*gpfdat &= ~((1<<4)|(1<<5)|(1<<6));
else
*gpfdat |= ((1<<4)|(1<<5)|(1<<6));
break;
case 1:
if(val==1)
*gpfdat &= ~((1<<4));
else
*gpfdat |= ((1<<4));
break;
case 2:
if(val==1)
*gpfdat &= ~((1<<5));
else
*gpfdat |= ((1<<5));
break;
case 3:
if(val==1)
*gpfdat &= ~((1<<6));
else
*gpfdat |= ((1<<6));
break;
}
return 0;
}
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE,
.open = first_drv_open,
.write = first_drv_write,
};
int major;
static struct class *firstdrv_class;
static struct device *firstdrv_device[4];
static int first_drv_init(void)
{
int i;
major = register_chrdev(0,"first_drv",&first_drv_fops);
firstdrv_class = class_create(THIS_MODULE,"firstdrv");
firstdrv_device[0] = device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"leds");
for(i=1;i<4;i++)
{
firstdrv_device[i] = device_create(firstdrv_class,NULL,MKDEV(major,i),NULL,"led%d",i);
}
gpfcon = (volatile unsigned long *)ioremap(0x56000050,16);
gpfdat = gpfcon + 1;
printk("first_drv_init\n");
return 0;
}
static void first_drv_exit(void)
{
iounmap(gpfcon);
unregister_chrdev(major,"first_drv");
device_destroy(firstdrv_class,MKDEV(major,0));
class_destroy(firstdrv_class);
printk("first_drv_exit\n");
}
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");
测试代码:
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[])
{
int fd;
char val;
char *filename;
if(argc!=3)
{
printf("Usage: \n");
printf("%s </dev/led?> <on|off>\n",argv[0]);
return 0;
}
filename = argv[1];
fd = open(filename,O_RDWR);
if(fd<0)
{
printf("can't open %s !\n",filename);
return 0;
}
if(strcmp(argv[2],"on")==0)
val = 1;
else if(strcmp(argv[2],"off")==0)
val = 0;
else
{
printf("Usage: \n");
printf("%s </dev/led?> <on|off>\n",argv[0]);
return 0;
}
write(fd,&val,1);
return 0;
}