[1] Review
1. Kernel module
entry and export license
2. Kernel print function
printk (print level "what you want to print");
printk ("what you want to print");
3. Module pass parameter
sudo insmod demo .ko a=100
/sys/module/driver name/
module_param (variable name, type, permission);
module_param_array (variable name, type, length, permission);
MODULE_PARM_DESC (variable, "string");
4.模块导出符号表
EXPORT_SYMBOL_GPL(变量名/函数名);
5.字符设备驱动
major = register_chrdev(major,name,&fops);
unregister_chrdev(major,name);
6.数据传输
copy_from_user();
copy_to_user();
7.地址映射
虚拟地址 = ioremap(物理地址,长度)
iounmap(虚拟地址);
练习:
1.使用字符设备驱动点灯
[2] ioctl uses
man ioctl
#include <sys/ioctl.h>
int ioctl(int fd, int request, ...);
功能:input /output control device
参数:
@fd :打开设备得到的文件描述符
@request:通过同宏_IO _IOR _IOW _IOWR封装的请求码
@... :可变参数
fops:long (*unlocked_ioctl) (struct file *,
unsigned int cmd, unsigned long args);
Parameters:
cmd: is the request
args passed from user space. args: is passed from user space...
应用层的ioctl被调用的时候驱动的fops中的
unlocked_ioctl就会被执行。
request这个32位请求码,每个部分的含义
31-30 00 - no parameters: uses _IO macro
10 - read: _IOR
01 - write: _IOW
11 - read/write: _IOWR
29-16 size of arguments
15-8 ascii character supposedly
unique to each driver
7-0 function #
#define _IO(type,nr)
_IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)
_IOC(_IOC_READ,(type),(nr),(sizeof(size)))
#define _IOW(type,nr,size)
_IOC(_IOC_WRITE,(type),(nr),(sizeof(size)))
#define _IOWR(type,nr,size)
_IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(sizeof(size)))
#define _IOC(dir,type,nr,size) \
(((dir) << _IOC_DIRSHIFT) | \
((type) << _IOC_TYPESHIFT) | \
((nr) << _IOC_NRSHIFT) | \
((size) << _IOC_SIZESHIFT))
2 14 8 8
dir size type nr
#define VFAT_IOCTL_READDIR_BOTH
_IOR('r', 1, struct dirent [2])
#define RED_ON _IO('a',0)
#define RED_OFF _IO('a',1)
PM:
Exercise:
1. Transfer char [50] string from user space to kernel space
, print it in the kernel
2. Pass this data from kernel space to user space, print and display
#define ACCESS_DATA_R _IOR('a',0,int)
#define ACCESS_DATA_W _IOW('a',0,int)
case ACCESS_DATA_R:
ret = copy_to_user((void *)args,&data,4);
break;
case ACCESS_DATA_W:
ret = copy_from_user(&data,(void *)args,4);
[1] git download code
1. Install git
sudo apt-get install git
2. Download command
git clone https://github.com/daizhansheng/Linux-driver-31.git
[2] The process of automatically creating a device node
user hotplug ---------------> udev/mdev
| |----->/dev/device node name
|/sys/class/ Directory/device name
--------|-------------------------------------
kernel |
|
1. Submit directory
class_create(owner, name)
2. Submit device name
struct device *device_create(struct class *class,
struct device *parent,dev_t devt, void *drvdata,
const char *fmt, …)
创建设备节点的函数接口讲解如下:
1.class_create(owner, name)
功能:提交目录
参数:
@owner :THIS_MODULE
(以后所有的驱动遇到owner就写THIS_MODULE,
编译驱动之后会生成一个文件,这个文件通过this_module
记录程序的入口和出口)
@name :目录名
返回值:成功返回struct class *的结构体指针,
失败返回错误码指针(void *)-5;
cls = class_create(owner, name);
思考如何判断这个函数返回的错误?
*((int *)ret) < 0 错误
sizeof(*ret) == 4; 错误
IS_ERR(cls)
如果返回值为真,表示他是一个错误,否则不是一个错误。
ERR_PTR(long error)
将错误码转化为错误码指针
PTR_ERR(const void *ptr)
将错误码指针转化为错误码
2.struct device *device_create(struct class *class,
struct device *parent,dev_t devt, void *drvdata,
const char *fmt, ...)
功能:提交设备节点名
参数:
@class :cls的结构体指针
@parent:NULL
@devt : 设备号
@drvdata:NULL
@fmt :设备节点的名字
返回值:成功返回struct device *的结构体指针,
失败返回错误码指针
MKDEV(ma,mi) //根据主次设备号合成设备号
MAJOR(dev) //根据设备号得到主设备号
MINOR(dev) //根据设备号得到次设备号
void class_destroy(struct class *cls)
功能:注销class
void device_destroy(struct class *class, dev_t devt)
功能:注销device