01_无硬件:
四个部分:头文件、两个入口函数初始化、注册入口函数、模块信息描述
//1.头文件
#include <linux/init.h>
#include <linux/module.h>
static dev_t no;
static unsigned count = 1;
static const char* name = "mydev";
static struct cdev mydev;
s
int myopen(struct inode *pi,struct file *pf)
{
return 0;
}
static const struct file_operations fops={
.open = myopen,
};
//2.函数init exit
static int myinit(void)
{
//向上: 内核相关
//1.注册
no = MKDEV(MA,MI);
ret = register_chrdev_region(no,count,name);
if(0!=ret)
return ret;
//2.初始化
cdev_init(&mydev,&fops);
//3.添加
ret = cdev_add(&mydev,no,count);
if(0!=ret)
{
unregister_chrdev_region(no,count);
return ret;
}
return 0;
}
static void myexit(void)
{
//删除设备
cdev_del(&mycdev);
//解除注册
unregister_chrdev_region(no,count);
return;
}
//3.注册入口函数
module_init(myinit)
module_exit(myexit);
//4.模块信息描述
MODULE_LICENSE("GPL");
有一些头文件没加,可以通过sourceinsight搜索函数查看头文件地址。
02_含硬件
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kdev_t.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include"cmd.h"
#include<asm/io.h>
#define MA 300
#define MI 0
#define GPF3CON 0x114001e0
#define GPF3DAT 0x114001e4
static dev_t no;
static unsigned count = 1;
static const char * name = "mydev";
static struct cdev mycdev;
static int* gpf3con = NULL;
static int* gpf3dat = NULL;
static long myioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
switch(cmd){
case(LED5_ON):
writel(readl(gpf3dat)|(0x1<<5),gpf3dat);
break;
case(LED5_OFF):
writel(readl(gpf3dat)&~(0x1<<5),gpf3dat);
break;
default:
printk("switch error");
}
return 0;
}
static const struct file_operations myfop = {
.unlocked_ioctl = myioctl,
};
static void addr_map(void)
{
gpf3con = ioremap(GPF3CON,4);
gpf3dat = ioremap(GPF3DAT,4);
}
static void dev_init(void)
{
writel((readl(gpf3con)&~(0xf<<20))|(0x1<<20),gpf3con);
writel(readl(gpf3dat)&~(0x1<<5),gpf3dat);
}
static void addr_unmap(void)
{
iounmap(gpf3con);
iounmap(gpf3dat);
}
static int myinit(void)
{
//注册
int ret = -1;
no = MKDEV(MA,MI);
ret = register_chrdev_region(no,count,name);
if(ret != 0)
return ret;
//初始化
cdev_init(&mycdev,&myfop);
//添加到内核
ret = cdev_add(&mycdev,no,count);
if(ret != 0)
{
unregister_chrdev_region(no,count);
return ret;
}
//硬件部分
addr_map();
dev_init();
return 0;
}
static void myexit(void)
{
cdev_del(&mycdev);
unregister_chrdev_region(no,count);
//硬件部分
addr_unmap();
}
module_init(myinit);
module_exit(myexit);
MODULE_LICENSE("GPL");
多了虚拟内存的映射和硬件设备初始化,在退出时解除初始化;利用file_operation的ioctl实现功能。
03_平台驱动和自动生成结点
#include<linux/init.h>
#include<linux/module.h>
#include<asm-generic/ioctl.h>
#include<linux/fs.h>
#include<linux/platform_device.h>
#include<asm/io.h>
#include<linux/cdev.h>
#include<linux/device.h>
#include<linux/of.h>
#include"cmd.h"
#define MA 300
#define MI 0
struct class *my_class = NULL;
struct device *my_dev;
static dev_t no;
static unsigned count=1;
static char* name ="mydev";
static struct cdev mydev;
static char *gpf3con = NULL;
static int *gpf3dat = NULL;
static long myioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
switch(cmd){
case(LED5_ON):
writel(readl(gpf3dat)|(0x1<<5),gpf3dat);
break;
case(LED5_OFF):
writel(readl(gpf3dat)&~(0x1<<5),gpf3dat);
break;
default:
printk("switch error\n");
}
return 0;
}
static const struct file_operations fops = {
.unlocked_ioctl = myioctl,
};
int ret = -1;
void addr_map(struct platform_device *pdev)
{
struct resource *rescon = platform_get_resource(pdev,IORESOURCE_MEM,0);
struct resource *resdat = platform_get_resource(pdev,IORESOURCE_MEM,1);
gpf3con = ioremap(rescon->start,4);
gpf3dat = ioremap(resdat->start,4);
}
void addr_unmap(void)
{
iounmap(gpf3con);
iounmap(gpf3dat);
}
int mydev_probe(struct platform_device *pdev)
{
no = MKDEV(MA,MI);
ret = register_chrdev_region(no,count,name);
if(ret!=0){
printk("register error\n");
return -1;
}
cdev_init(&mydev,&fops);
ret = cdev_add(&mydev,no,count);
if(ret!=0){
printk("add error\n");
unregister_chrdev_region(no,count);
return -1;
}
addr_map(pdev);
//利用class_create函数和device_create函数创建设备结点
my_class = class_create(THIS_MODULE,"fs_class");
if(IS_ERR(my_class)){
printk("Err:failed in creating class\n");
cdev_del(&mydev);
unregister_chrdev_region(no,count);
addr_unmap();
return -1;
}
my_dev = device_create(my_class,NULL,no,NULL,name);
if(IS_ERR(my_dev)){
printk("Err:failed in creating device\n");
cdev_del(&mydev);
unregister_chrdev_region(no,count);
addr_unmap();
class_destroy(my_class);
return -1;
}
return 0;
}
int mydev_drv_remove(struct platform_device *pdev)
{
//删除自动生成的结点
device_destroy(my_class,no);
class_destroy(my_class);
cdev_del(&mydev);
unregister_chrdev_region(no,count);
addr_unmap();
return 0;
}
//平台驱动的注册,移植方式参考其他文件
//在dm9000.c找到移植过来,ctrl+f替换dm9000为mydev
#ifdef CONFIG_OF
static const struct of_device_id mydev_of_matches[] = {
{ .compatible = "fs,myled", }, ///和设备树匹配
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mydev_of_matches);
#endif
static struct platform_driver mydev_driver = {
.driver = {
.name = "mydev",
.owner = THIS_MODULE,
//电源不要 .pm = &mydev_drv_pm_ops,
.of_match_table = of_match_ptr(mydev_of_matches),
},
.probe = mydev_probe,
.remove = mydev_drv_remove,
};
module_platform_driver(mydev_driver);
MODULE_LICENSE("GPL");
自动创建结点时,创建类失败则要删除设备、解除注册、取消映射,创建设备失败多一步删除类。
平台总线(包含平台设备和平台驱动)注册是移植过来的。
03_01平台驱动概念
正常设备系统的组成有四个关键:设备、驱动、总线、类
在总线上挂载设备和驱动,运行设备时,通过总线的方式找到对应驱动。其中相似的设备可以通过定义为一类来优化重复代码框架。有的设备没有总线,则用软件模拟总线,这就是平台总线。
平台设备:修改设备树文件(dts文件)添加平台设备,在驱动中和of_device_id里的.compatible的设备名字匹配
例如(芯片是armv7架构coretex a9系类的exyno4412)LED5:
myled@114001e0{
compatible = "fs,myled"; //编,和驱动匹配
//reg = <0x114001e0 0x4>,<0x114001e4 0x4>; //地址,大小
reg = <0x114001e0 0x8>;
}
key2外部中断:
compatible = "fs,mykey";
interrupt-parent = <&gpx1>;
interrupts = <1 2>;
};
平台驱动:跟file_operations相似,用.probe和.remove替换两个入口函数。
04_字符设备驱动练习
①
1.
写
rtc
驱动
2.
main
里面间隔
1s
刷新显示时钟
②
1.
写
rtc
驱动
2.
mian
里面写个
socket
服务器,通过
socket
每个
1s
传输一次时钟
3.
ubuntu
写一个客户端,接收对方传输过来的数据,并实时显示。
③
使用按键k3,按下开灯,按下关灯。(按键和led必须是2个独立的ko;一个main,2个独
立设备关联起来;)
④
蜂鸣器响和不响; 两只老虎(改变周期频率
100M/1/2/CNT = 527
)
第一个是硬件框架,第二个是结合网络编程,第三个是两个模块结合,第四个是利用音频(不同音阶对应不同频率)。
第④个:
应用程序:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/ioctl.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define LED_ON _IO('L', 0x01)
#define LED_OFF _IO('L', 0x02)
#define BUZZER_ON _IO('B', 1)
#define BUZZER_OFF _IO('B', 2)
#define BUZZER_SET _IOW('B', 3, int)
#define Do 262
#define Re 294
#define Mi 330
#define Fa 349
#define So 392
#define La 440
#define Xi 494
int main(void)
{
//int data[7] = {Do, Re, Mi, Fa, So, La, Xi};
int data[] = {
Mi, So, So, Xi, Mi, Do, Do, Re, Mi, Mi, Re, Do,
Mi, So, So, Xi, Mi, Do, Do, Re, Mi, Mi, Re, Do,
Mi, So, So, Xi, Mi, Do, Do, Re, Mi, Mi, Re, Do,
Mi, So, So, Xi, Mi, Do, Do, Re, Mi, Mi, Re, Do,
Fa, Fa, Fa, Xi, So, So, Re,
Mi, So, So, Xi, Mi, Do, Do, Re, Mi, Mi, Re, Re, Do
};
int led_fd;
int key_fd;
int buzzer_fd;
int ret;
buzzer_fd = open("/dev/buzzer", O_RDWR);
if (buzzer_fd < 0)
{
perror("open buzzer");
return -1;
}
int i;
ioctl(buzzer_fd, BUZZER_ON);
//while (1)
//{
for (i = 0; i < sizeof(data)/sizeof(data[0]); i++)
{
ioctl(buzzer_fd, BUZZER_SET, data[i]);
usleep(1000*100);
}
ioctl(buzzer_fd, BUZZER_OFF);
// ioctl(buzzer_fd, BUZZER_ON);
// sleep(1);
// ioctl(buzzer_fd, BUZZER_OFF);
// sleep(1);
//}
close(buzzer_fd);
return 0;
}
驱动:
// 内核模块
// 基于内核模块 实现平台驱动框架
// 基于平台驱动框架 添加字符设备驱动框架
// 基于字符设备驱动框架 实现各种驱动
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/kdev_t.h>
#include <asm/uaccess.h>
#define BUZZER_ON _IO('B', 1)
#define BUZZER_OFF _IO('B', 2)
#define BUZZER_SET _IOW('B', 3,int)
dev_t dev_no;
struct cdev key_cdev;
char *device_name = "buzzer";
char *class_name = "buzzer_class";
struct class *buzzer_class;
struct device *buzzer_drv;
unsigned int *vir_con;
unsigned int *vir_tcfg0;
unsigned int *vir_tcfg1;
unsigned int *vir_tcon;
unsigned int *vir_tcntb0;
unsigned int *vir_tcmpb0;
#define LA 440
long buzzer_ioctl (struct file *, unsigned int, unsigned long);
const struct file_operations led_fops = {
.unlocked_ioctl = buzzer_ioctl,
};
// 无源蜂鸣器 相关操作
void buzzer_add_umap(void);
void buzzer_config(void);
void buzzer_start(void);
void buzzer_stop(void);
int dev_probe(struct platform_device *dev);
int dev_remove(struct platform_device *dev);
static const struct of_device_id led_of_match[] = {
{.compatible = "fs,buzzer"},
{}};
struct platform_driver buzzer_driver =
{
.driver = {
.name = "fs,buzzer",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(led_of_match),
},
.probe = dev_probe,
.remove = dev_remove,
};
static int mod_init(void)
{
return platform_driver_register(&buzzer_driver);
}
static void mod_exit(void)
{
return platform_driver_unregister(&buzzer_driver);
}
module_init(mod_init);
module_exit(mod_exit);
MODULE_LICENSE("GPL");
int dev_probe(struct platform_device *dev)
{
printk("11111\n");
struct resource *res_con;
struct resource *res_pwm;
// 创建字符设备
int ret;
// 申请设备号
ret = alloc_chrdev_region(&dev_no, 0, 1, device_name);
if (ret < 0)
{
goto alloc_failed;
}
// cdev_init
cdev_init(&key_cdev, &led_fops);
// cdev_add
ret = cdev_add(&key_cdev, dev_no, 1);
if (ret < 0)
{
goto cdev_add_failed;
}
// class_create
buzzer_class = class_create(THIS_MODULE, class_name);
if (IS_ERR(buzzer_class))
{
goto class_create_failed;
}
// device_create
buzzer_drv = device_create(buzzer_class, NULL, dev_no, NULL, device_name);
if (IS_ERR(buzzer_drv))
{
goto device_create_failed;
}
// 从设备树获取硬件资源(寄存器地址)
res_con = platform_get_resource(dev, IORESOURCE_MEM, 0);
res_pwm = platform_get_resource(dev, IORESOURCE_MEM, 1);
if (res_con == NULL || res_pwm == NULL)
{
goto platform_get_resource;
}
printk("config:%#x data:%#x\n", res_con->start, res_pwm->start);
vir_con = (unsigned int *)ioremap(res_con->start, 4);
if (vir_con == NULL)
{
goto ioremap_con_failed;
}
vir_tcfg0 = (unsigned int *)ioremap(res_pwm->start, 4);
if(vir_tcfg0 == NULL){
goto ioremap_tcfg0_failed;
}
vir_tcfg1 = (unsigned int *)ioremap(res_pwm->start+0x4, 4);
if(vir_tcfg1 == NULL){
goto ioremap_tcfg1_failed;
}
vir_tcon = (unsigned int *)ioremap(res_pwm->start+0x8, 4);
if(vir_tcon == NULL){
goto ioremap_tcon_failed;
}
vir_tcntb0 = (unsigned int *)ioremap(res_pwm->start+0xc, 4);
if(vir_tcntb0 == NULL){
goto ioremap_tcntb0_failed;
}
vir_tcmpb0 = (unsigned int *)ioremap(res_pwm->start+0x10, 4);
if(vir_tcmpb0 == NULL){
goto ioremap_tcmpb0_failed;
}
buzzer_config();
// buzzer_start();
return 0;
ioremap_tcmpb0_failed:
iounmap(vir_tcntb0);
ioremap_tcntb0_failed:
iounmap(vir_tcon);
ioremap_tcon_failed:
iounmap(vir_tcfg1);
ioremap_tcfg1_failed:
iounmap(vir_tcfg0);
ioremap_tcfg0_failed:
iounmap(vir_con);
ioremap_con_failed:
platform_get_resource:
device_destroy(buzzer_class, dev_no);
device_create_failed:
class_destroy(buzzer_class);
class_create_failed:
cdev_del(&key_cdev);
cdev_add_failed:
unregister_chrdev_region(dev_no, 1);
alloc_failed:
return -1;
}
int dev_remove(struct platform_device *dev)
{
// 销毁字符设备
buzzer_add_umap();
// device_destroy
device_destroy(buzzer_class, dev_no);
// class_destroy
class_destroy(buzzer_class);
// cdev_del
cdev_del(&key_cdev);
// 释放设备号
unregister_chrdev_region(dev_no, 1);
return 0;
}
void buzzer_add_umap(void)
{
iounmap(vir_con);
iounmap(vir_tcfg0);
iounmap(vir_tcfg1);
iounmap(vir_tcon);
iounmap(vir_tcntb0);
iounmap(vir_tcmpb0);
}
void buzzer_config(void)
{
*vir_con = (*vir_con) & ~(0xf);
*vir_con = (*vir_con) | (0x2); //配置为pmw功能
*vir_tcfg0 |= 0xff; //预分配256
*vir_tcfg1= *vir_tcfg1 &(~0xf);
*vir_tcfg1 = *vir_tcfg1 | (0x3); // 8分频
//F_tin = 100Mhz / 256 /8 = 48828hz
*vir_tcntb0 = 48828/LA;
*vir_tcmpb0 = (48828/LA)/2;
*vir_tcon = *vir_tcon|(0x1<<3);
*vir_tcon = (*vir_tcon) | (0x1<<1);
*vir_tcon = (*vir_tcon) & ~(0x1<<1);
}
void buzzer_set_freq(int freq)
{
*vir_tcntb0 = 48828/freq;
*vir_tcmpb0 = (48828/freq)/2;
}
void buzzer_start(void)
{
*vir_tcon = (*vir_tcon) | (0x1);
}
void buzzer_stop(void)
{
*vir_tcon = (*vir_tcon) & ~(0x1);
}
long buzzer_ioctl (struct file *pf, unsigned int cmd, unsigned long arg)
{
switch (cmd)
{
case BUZZER_ON:
printk("buzzer on\n");
buzzer_start();
break;
case BUZZER_OFF:
printk("buzzer off\n");
buzzer_stop();
break;
case BUZZER_SET:
buzzer_set_freq(arg);
break;
default:
break;
}
return 0;
}