韦东山第12课-字符设备驱动框架、led驱动

本文介绍了一个简单的字符设备驱动实现过程,包括驱动初始化、文件操作、设备号注册与卸载等内容,并详细展示了如何通过mdev机制自动创建设备文件,以及如何进行LED控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 first_drv驱动函数

first_drv.c:

#include <linux/types.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/mc146818rtc.h>
#include <linux/netdevice.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/delay.h>
static int first_drv_open(struct inode *inode, struct file *file)
{
    printk("first_drv_open \n");
    return 0;
 }
static ssize_t first_drv_open(struct file *file, const char __user *buf, size_t count, loff_t *ppos);
{
    printk("first_drv_write \n");
    return 0;
    
   }
static const struct file_operations first_drv_fops = {
	.owner		 = THIS_MODULE,		/* owner */
	.write		 = first_drv_write,	/* write */
	.open	 	=first_drv_open,		/* open */
	
};
static int __init first_drv_init(void)
{
	register_chrdev(111,"first_drv",&first_drv_fops);//registe dev;rell kernel
/* 这是有对比空缺的设备号填写的,也可以用
int major=register_chrdev(0,"first_drv",&first_drv_fops); //填0表示自动分配设备号
*/
                 //主设备号,名字,file_operatins结构体     
}
static void __exit first_drv_exit(void)
{
 	unregister_device(111,"first_drv");//卸载函数
 	              //主设备号,设备名
}
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(IBM_FS3270_MAJOR);





Makefile:

KERN_DIR = /home/book/workspace/jz2440/systems/linux-2.6.22.6
all:
 	make -C $(KERN_DIR) M='pwd'  modules
clean:
	make -C $(KERN_DIR) M='pwd' module clean
	rm -rf modules.order
obj-m   += first_drv.o





加载驱动:

cat /proc/devices   //查看驱动

insmod SHT21.ko  //装置驱动 调用module_init(first_drv_init);

rmmod first_drv //卸载驱动调用module_exit(first_drv_init);

lsmod //查看内核中已经加载了哪些模块

mknod /dev/led c 111 0 // 创建设备文件

2 自动创建设备文件

int major=register_chrdev(0,"first_drv",&first_drv_fops); //填0表示自动分配设备号
udev机制,我们就是mdev,
cd /sys/ 目录下有系统设备的信息
mdev根据系统信息创建设备节点。
在驱动注册函数中加入如下代码:
firstdrv_class = class_create(THIS_MODULE, "firstdrv");
firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
在驱动卸载函数中加入如下代码:
class_device_unregister(firstdrv_class_dev);class_destroy(firstdrv_class);
这个时候驱动加载进去就会自动创建设备文件。
问:为什么可以自动创建呢:
答:驱动程序通过这两个函数,给内核提供了系统信息。一加载就会有文件生成 /sys/class/firstdrv/xyz/dev   mdev根据这些信息创建设备。
问:为什么mdev机制可以根据/sys目录文件的改变生成文件呢?
答:/etc/init.d/rcS 中有“echo /sbin/mdev > /proc/sys/kernel/hotplug” 表示支持热插拔:有驱动加载,就调用mdev
我的系统竟然不支持热插拔,
1 构建根文件系统时busybox 配置为支持hotplug
2 /etc/init.d/rcS 中加入“echo /sbin/mdev > /proc/sys/kernel/hotplug”等
程序框架:
//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/uaccess.h>
#include <asm/irq.h>
#include <asm/io.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");
	/* 配置GPF4,5,6为输出 */
	return 0;
}

static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
	printk("first_drv_write\n");
	return 0;
}

static struct file_operations first_drv_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   first_drv_open,     
    .write	=  first_drv_write,	   
};


int major;
static int __init first_drv_init(void)
{
	 major=register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核
                 // (主设备号,驱动名字(cat /proc/devices中显示),file_operations类)

	firstdrv_class = class_create(THIS_MODULE, "firstdrv");
                //(THIS_MODULE,驱动名字)
	firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
              //(firstdrv_class类,父类,驱动号:MKDEV(主号,次号)),struct device *device,mdev要创建的设备名称)
	return 0;
}

static void first_drv_exit(void)
{
	unregister_chrdev(major, "first_drv"); // 卸载

	class_device_unregister(firstdrv_class_dev);
	class_destroy(firstdrv_class);
}

module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");


3 led驱动

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>

static struct class *firstdrv_class;
static struct class_device	*firstdrv_class_dev;

volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;


static int first_drv_open(struct inode *inode, struct file *file)
{
        *gpfcon &= ~((3<<4*2)|(3<<5*2)|(3<<6*2));
        *gpfcon |= ((1<<4*2)|(1<<5*2)|(1<<6*2));
      //  *gpfdat=((1<<4)|(1<<5)|(1<<6));
    //printk("first_drv_open\n");
	/* 配置GPF4,5,6为输出 */
	return 0;
}

static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
    int val;
    copy_from_user(&val,buf,count);
   if(val==1)
   {
    //电灯
    *gpfdat &=~((1<<4)|(1<<5)|(1<<6));
    }
   else
   {
    //down
    *gpfdat |=((1<<4)|(1<<5)|(1<<6));
    }

    //printk("first_drv_write\n");
	return 0;
}

static struct file_operations first_drv_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   first_drv_open,     
    .write	=  first_drv_write,	   
};


int major;
static int __init first_drv_init(void)
{
	 major=register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核

	firstdrv_class = class_create(THIS_MODULE, "firstdrv");

	firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */

	gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
	gpfdat = gpfcon + 1;

	return 0;
}

static void first_drv_exit(void)
{
	unregister_chrdev(major, "first_drv"); // 卸载

	class_device_unregister(firstdrv_class_dev);
	class_destroy(firstdrv_class);
	//iounmap(gpfcon);
}

module_init(first_drv_init);
module_exit(first_drv_exit);


MODULE_LICENSE("GPL");


4 copy_from_user

copy_from_user
static inline int copy_from_user(void *to, const void __user *from, int n)
例子:.ko/first_drv_write:copy_from_user(&val, buf, count);  //从buf考值到val中

测试程序用write写数据到驱动first_drv_write
copy_to_user

static inline int copy_to_user(void __user *to, const void *from, int n)


例子:.ko/second_drv_read:copy_to_user(buf, key_vals, sizeof(key_vals)); //将key_cals的值考入buf中
            seconddrvtest.c:read(fd, key_vals, sizeof(key_vals));


4 测试函数

firstdrvtest.c
firstdrvtest.c:  //通过操作文件/dev/led  使用驱动,open write与驱动中的对应
昨天编译一直不能用是因为用的gcc 编译的 应该用arm-linux-gcc -o firstdrvtest firstdrvtest.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

/* firstdrvtest on
  * firstdrvtest off
  */
int main(int argc, char **argv)
{
	int fd;
	int val = 1;
	fd = open("/dev/xxx", O_RDWR);
	if (fd < 0)
	{
		printf("can't open!\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;
}











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值