s3c_led.c分析:http://blog.csdn.net/hurry_liu/article/details/8770206 1,注册设备
int register_chrdev_region(dev_t from, unsigned count, const char *name)
动态分配设备号
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name) // 无法再安装驱动前创建设备文件,因为安装前没有分配设备号;安装驱动后,从/proc/devices中查询设备号 释放设备号 void unregister_chrdev_region(dev_t from, unsigned count) 2,重要的结构体 <1>struct file       <linux/fs.h> //系统中每个打开的文件在内核空间都有一个关联的struct file。打开文件时创建,关闭时创建 重要成员: loff_t f_pos //文件读写位置
struct file_operations *f_op void *private_data; <2>struct file_operations            <linux/fs.h>
//操作函数指针的集合,每个指针表示用户空间能对设备文件进行的操作(其中的各函数是最主要的工作)
重要成员: int (*open)(struct inode *, struct file *)  //初始化设备和标明次设备号 void (*release)(struct inode *,struct file *) //关闭设备 ssize_t (*read)(sruct file *flip, char __user *buff, size_t count, loff_t *offp)  //从设备中读取数据到用户空间,offp文件当前的访问位置  //buff指向数据缓存, 是指向用户空间,不能被内核代码直接引用 count传输的数据量  (这两个参数由用户空间提供) ssize_t (*write)(sruct file *, const char __user *buff, size_t, loff_t *)  //将数据传递给驱动程序 int (*ioctl)(struct inode *, srtuct file *, unsigned int, unsigned long)    //控制设备 off_t (*llseek)(struct file *, loff_t, int) <3>struct inode结构   
//记录文件的物理上的信息,一个文件只有一个inode结构
重要成员: dev_t  i_rdev;    //设备号(对于表示设备文件的inode) struct  *cdev *i_cdev;    //当inode指向字符设备文件时

可以从inode中获取主次设备号

MAJOR(dev_t dev)

MINOR(dev_t dev)

MKDEV(int major,int minor)   //通过主次设备号来生成dev_t

 

<4>struct cdev结构                 <linux/cdev.h> //内核使用cdev结构体描述字符设备 重要成员:

struct module *owner;    //所属模块

const struct file_operations *ops;  

dev_t dev;          //设备号

 

3,一些函数

<1>用来对cdev结构操作的函数:

void cdev_init(struct cdev *, const struct file_operations *);

//初始化,建立cdev和file_operation 之间的连接

struct cdev *cdev_alloc(void); //动态申请一个cdev内存

void cdev_put(struct cdev *p); //释放

int cdev_add(struct cdev *, dev_t, unsigned); //注册设备,通常发生在驱动模块的加载函数中

void cdev_del(struct cdev *);//注销设备,通常发生在驱动模块的卸载函数中

 

<2>内核提供专门的函数用于访问用户空间的指针

int copy_from_user(void *to, const void __user *from,int n)   write() int copt_to_user(void __user *to, const void *from, int n)    read() <3>宏定义request_mem_region和ioremap     include/linux/ioport.h request_mem_region(S3C_GPB_BASE, S3C_GPB_LEN,DEV_NAME)) /* 申请内存。注意:这里的内存是FL2440中实际的物理内存,他对应了与LED的相关的寄存器 占用起始物理地址S3C_GPB_BASE之后的连续S3C_GPB_LEN字节大小空间 该函数并没有做实际性的映射工作,只是告诉内核要使用一块内存地址,声明占有,也方便内核管理这些资源。*/ fl2440_gpb_membase=ioremap(S3C_GPB_BASE, S3C_GPB_LEN)      ioremap()在mm/ioremap.c中定义,返回void型指针 //主要是检查传入地址的合法性,建立页表(包括访问权限),完成物理地址到虚拟地址的转换。 release_mem_region(S3C_GPB_BASE, S3C_GPB_LEN) void iounmap(fl2440_gpb_membase); //一些宏定义 __raw_writel((val),(reg)+s3c_gpb_membase) __raw_readll((reg)+s3c_gpb_membase)   //读操作和写操作寄存器 4,led字符设备 //字符设备用struct cdev来描述;struct cdev *led_cdev;
加载模块int __init s3c_led_init(): <1>硬件初始化:s3c_hw_init(): 申请内存request_mem_region()–> 建立物理内存到虚拟内存的映射ioremap()–>初始化硬件设备 <2>申请设备号: <3>为cdev分配内存(定义为指针式需要) led_cdev = cdev_alloc(); //可以将cdev结构嵌入到自己的设备特定结构中 <4>初始化cdev   led_cdev->owner = THIS_MODULE; cdev_init(led_cdev, &led_fops);    //将led_cdev和file_operations挂钩 <5>,添加cdev     result = cdev_add(led_dev, devno, dev_count);  //将设备号和设备挂钩   devno是设备号 //在驱动程序准备好处理设备上的操作时,调用该函数 <6>出错处理[函数] 卸载模块s3c_led_exit(): <1>调用s3c_hw_term()  : 关闭led–>释放内存release_mem_region()–>解除映射关系iounmap() <2>注销设备:cdev_del() <3>释放设备号:unregister_chrdev_region() 设备操作的实现 定义struct file_operations led_fops = { .owner = THIS_MODULE; .open = led_open; .release = led_release; .unlocked_ioctl = led_ioctl;
}; 三,arm开发板上 tftp -gr s3c_hello.ko 192.168.1.3 insmod,rmmod      //加载和卸载模块命令(root权限) lsmod     //列举内核模块及引用计数  cat /proc/modules insmod s3c_led.ko cat /proc/drivers cd dev/  mknod -m666 c led0  252 0 mknod -m666 c led1  252 1 mknod -m666 c led2  252 2 mknod -m666 c led3  252 3 ./test_led /dev/led0 1     //点亮第一个灯

转载于:https://www.cnblogs.com/zhoutian220/p/3965100.html