led设备驱动(s3c_led.c)

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