自动挂接根文件系统(直接从NFS启动)
- 修改uboot命令行
把 bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0,115200
改为:
set bootarges noinitrd root=/dev/nfs nfsroot=192.168.0.104:/home/book/work/nfs_root/first_fs ip=192.168.0.10:192.168.0.104:192.168.0.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0,115200
在uboot中设置如下:
setenv bootargs noinitrd root=/dev/nfs console=ttySAC0 nfsroot=192.168.0.104:/home/book/work/nfs_root/first_fs ip=192.168.0.3:192.168.0.104:192.168.0.1:255.255.255.0::eth0:off init=/linuxrc
设置NFS启动参数 :
- 设置服务器IP,目录
- 设置自己的IP
运行第一个驱动程序
insmod first_drv.ko 挂载first_drv.ko
cat /proc/devices 查看当前挂载了的驱动
增加一个测试程序
include <sys/types.h> include <sys/stat.h> include <fcntl.h> include <stdio.h> int main(int argc, char **argv) { int fd; int val = 1; fd = open("/dev/xxx", O_RDWR);//打开 if (fd < 0) { printf("can't open! "); } write(fd,&val,4);//写 return 0; }
编译测试程序
arm-linux-gcc -o first_dev_test first_dev_test.c
提示不能打开当前测试文件是因为不存在/dev/xxx这个文件
所以需要创建这个设备节点
创建设备节点使用mknod 命令
mknod /dev/xxx c 111 0
命令 设备节点 设备类型(字符型) 主设备号(111) 次设备号
挂载设备 命令:insmod xxx(设备名称)
卸载设备命令:rmmod xxx(设备名称)
查看设备命令:lsmod
cat /proc/devices 查看当前挂载了的驱动
自动分配主设备号
在注册 register_chrdev函数中设备号位置填写0 系统将在自动分配设备号
1驱动程序里面可以自动分配主设备号 也可以手动指定设备号
- 应用程序去打开一个open("/dev/xxx", O_RDWR); 使用 mknod /dev/xxx c 主设备号 次设备号 手动创建
- 使用自动创建 使用udev机制 就是busybox 中的mdev机制自动创建 (根据系统信息创设备节点)
定义一个类和一个设备
static struct class *firstdrv_class;
static struct class_device *firstdrv_class_dev;
在初始化设备时添加
- 使用class_create创建一个firstdrv_class类
firstdrv_class = class_create(THIS_MODULE,"firstdrv");//创建一个 first_class 这样的一个类
- 使用class_device_create创建一个设备 设备节点是 xyz
firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); //mdev 自动创建一个 /dev/xyz */
int frist_drv_init(void) { major= register_chrdev(0,"frist_drv",&first_drv_fops);//注册函数(注册驱动程序) (major 主设备号 )告诉内核 //主设备号 驱动名称 file_operations结构 /*生成系统信息*/ firstdrv_class = class_create(THIS_MODULE,"firstdrv");//创建一个 first_class 这样的一个类 firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); //mdev 自动创建一个 /dev/xyz */ return 0; }
在出口函数中添加如下
- 使用class_device_unregister 函数来卸载 class_device_create创建的设备
class_device_unregister(firstdrv_class_dev);
- 使用class_destroy 释放 掉firstdrv_class这个类
class_destroy(firstdrv_class);
void frist_drv_exit(void) { unregister_chrdev(major,"frist_drv");//卸载函数 卸载驱动 class_device_unregister(firstdrv_class_dev); class_destroy(firstdrv_class); }
实现点亮LED灯功能
硬件操作
映射虚拟地址 :使用 ioremap(off,sz) 函数 映射
/*将设备的物理地址映射为虚拟地址*/
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); //0x56000050 (物理 地址) 16(长度)
gpfdat = gpfcon+1; //0x56000050+4 = 0x56000054 (物理地址) 16(长度)
取消虚拟地址映射:iounmap(_addr);
iounmap(gpfcon);
iounmap(gpfcon+1);
内核和用户之间参数传递 :
copy_from_user的功能是从用户空间传递数据到内核空间
对应的 copy_to_user 从内核空间传递数据到 用户空间
copy_from_user(&val,buf,count);
- 配置 GPFCON open
static int first_drv_open(struct inode *inode, struct file *file) { //printk("first_drv_open "); /*配置GPFCON 4 5 6为输出*/ *gpfcon &=~((0x3<<(4*2))|(0x3<<(5*2))|(0x3<<(6*2)));//清除GPFCON 4 5 6对应位 *gpfcon |= ((0x1<<(4*2))|(0x1<<(5*2))|(0x1<<(6*2)));//设置 GPFCON 4 5 6 为输出 return 0; }
- 设置 GPFDAT write
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { int val; //printk("first_drv_write "); copy_from_user(&val,buf,count);//将buf中的数据拷贝到 val中 val的值等于 *buf的前0-count的数据 copy_from_user的功能是从用户空间传递数据到内核空间 // 对应的 copy_to_user 从内核空间传递数据到 用户空间 if(val == 0) { *gpfdat |= ((1<<4)|(1<<5)|(1<<6));// 熄灭灯 } else { *gpfdat &= ~((1<<4)|(1<<5)|(1<<6));//打开led } return 0; }
使用次设备号:
主设备号 是建立与内核的链接,次设备号 是用来给驱动使用的