1、驱动操作硬件的方法
驱动操作硬件时需要用到硬件物理地址在内核虚拟地址空间相对应的虚拟地址
2、内核虚拟地址映射方法
静态映射:内核移植时直接使用编码的方式,如果需要修改地址映射则要修改内核源代码,还要重新编译内核。
动态映射:驱动根据需要动态建立映射。
3、静态映射操作LED
struct cdev s_cdev; /* 字符设备 */
dev_t s_devNo; /* 设备号 */
#define GPJ0CON *((volatile unsigned int *)S5PV210_GPJ0CON) //GPJ0CON映射的虚拟地址
#define GPJ0DAT *((volatile unsigned int *)S5PV210_GPJ0DAT) //GPJ0DAT映射的虚拟地址
static int LED_test_open(struct inode *inode, struct file *file)
{
return 0;
}
static int LED_test_release(struct inode *inode, struct file *file)
{
return 0;
}
//应用层和驱动之间的数据交换,copy_from_user:从用户空间复制到内核空间,copy_to_user:从内核空间复制到用户空间
static ssize_t LED_test_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
char kBuf[1024] = "module_test_read";
int ret = copy_to_user(ubuf, kBuf, count);
if (ret == 0)
printk(KERN_DEBUG, "copy_to_user success");
return count;
}
static ssize_t LED_test_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos)
{
char kBuf[1024];
int ret = copy_from_user(kBuf, ubuf, count);
if (ret == 0)
printk(KERN_DEBUG, "copy_from_user success");
GPJ0CON = 0x11111111;
if (kBuf[0] = '1')
GPJ0DAT = (0 << 3);
else if (kBuf[0] = '0')
GPJ0DAT = (1 << 3);
return count;
}
struct file_operations LED_test_fops =
{
.owner = THIS_MODULE,
.read = LED_test_read,
.write = LED_test_write,
.open = LED_test_open,
.release = LED_test_realse,
};
static int __init chrdev_LED_init(void)
{
//printk是内核源码中用来打印信息的函数,KERN_DEBUG是打印级别
printk(KERN_DEBUG "chrdev_LED_init");
/*************** 动态申请设备号 *****************/
int ret = alloc_chrdev_region(&s_devNo, 0, 1, "LED_MODULE_TEST");
if (ret < 0)
{
printk(KERN_ERR "alloc_chrdev_region failed");
return -1;
}
/*************** 注册字符设备驱动 **************/
//建立cdev和fops连接
s_cdev.owner = THIS_MODULE;
s_cdev.fops = LED_test_fops;
cdev_init(&s_cdev, &LED_test_fops);
//向内核注册字符设备驱动
ret = cdev_add(&s_cdev, s_devNo, 1);
if (ret < 0)
{
printk(KERN_ERR "cdev_add failed");
unregister_chrdev_region(s_devNo, 1);
retun -1;
}
return 0;
}
static void __exit chrdev_LED_exit(void)
{
printk(KERN_DEBUG "chrdev_LED_exit");
//注销字符设备驱动
cdev_del(&s_cdev);
//释放设备号
unregister_chrdev_region(s_devNo, 1);
}
module_init(chrdev_LED_init);
module_exit(chrdev_LED_exit);
//宏定义
MODULE_LICENSE("GPL") //模块的许可证
MODULE_AUTHOR("xy_L") //模块的作者
MODULE_DESCIPTION("chrdev LED module") //模块的描述
MODULE_ALIAS("LED_test") //模块的别名
4、动态映射操作LED
(1)建立动态映射
//向内核申请需要映射的内存资源
#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name), 0)
//检查物理地址的合法性,建立页表(包括访问权限),完成物理地址到虚拟地址的转换
void * ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags);
(2)销毁动态映射
//释放内存资源
#define release_mem_region(start,n) __release_region(&iomem_resource, (start), (n))
//取消ioremap的映射
void iounmap(void * addr);
//需要先iounmap再进行release_mem_region
(3)示例
struct cdev s_cdev; /* 字符设备 */
dev_t s_devNo; /* 设备号 */
#define GPJ0CON 0xE0200240 //GPJ0CON物理地址
#define GPJ0DAT 0xE0200244 //GPJ0DAT物理地址
unsigned int *pGPJ0CON;
unsigned int *pGPJ0DAT;
static int LED_test_open(struct inode *inode, struct file *file)
{
/***************** 动态映射操作寄存器 *************/
//动态映射GPJ0CON
if (!request_mem_region(GPJ0CON, 4, "GPJ0CON"))
return -EINVAL;
pGPJ0CON = ioremap(GPJ0CON, 4);
//动态映射GPJ0DAT
if (!request_mem_region(GPJ0DAT, 4, "GPJ0DAT"))
return -EINVAL;
pGPJ0DAT = ioremap(GPJ0DAT, 4);
return 0;
}
static int LED_test_release(struct inode *inode, struct file *file)
{
// 解除映射
iounmap(pGPJ0CON);
release_mem_region(GPJ0CON, 4);
iounmap(pGPJ0DAT);
release_mem_region(GPJ0DAT, 4);
return 0;
}
//应用层和驱动之间的数据交换,copy_from_user:从用户空间复制到内核空间,copy_to_user:从内核空间复制到用户空间
static ssize_t LED_test_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
char kBuf[1024] = "module_test_read";
int ret = copy_to_user(ubuf, kBuf, count);
if (ret == 0)
printk(KERN_DEBUG, "copy_to_user success");
return count;
}
static ssize_t LED_test_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos)
{
char kBuf[1024];
int ret = copy_from_user(kBuf, ubuf, count);
if (ret == 0)
printk(KERN_DEBUG, "copy_from_user success");
*pGPJ0CON = 0x11111111;
if (kBuf[0] = '1')
*pGPJ0DAT = (0 << 3);
else if (kBuf[0] = '0')
*pGPJ0DAT = (1 << 3);
return count;
}
struct file_operations LED_test_fops =
{
.owner = THIS_MODULE,
.read = LED_test_read,
.write = LED_test_write,
.open = LED_test_open,
.release = LED_test_realse,
};
static int __init chrdev_LED_init(void)
{
//printk是内核源码中用来打印信息的函数,KERN_DEBUG是打印级别
printk(KERN_DEBUG "chrdev_LED_init");
/*************** 动态申请设备号 *****************/
int ret = alloc_chrdev_region(&s_devNo, 0, 1, "LED_MODULE_TEST");
if (ret < 0)
{
printk(KERN_ERR "alloc_chrdev_region failed");
return -1;
}
/*************** 注册字符设备驱动 **************/
//建立cdev和fops连接
s_cdev.owner = THIS_MODULE;
s_cdev.fops = LED_test_fops;
cdev_init(&s_cdev, &LED_test_fops);
//向内核注册字符设备驱动
ret = cdev_add(&s_cdev, s_devNo, 1);
if (ret < 0)
{
printk(KERN_ERR "cdev_add failed");
unregister_chrdev_region(s_devNo, 1);
retun -1;
}
return 0;
}
static void __exit chrdev_LED_exit(void)
{
printk(KERN_DEBUG "chrdev_LED_exit");
//注销字符设备驱动
cdev_del(&s_cdev);
//释放设备号
unregister_chrdev_region(s_devNo, 1);
}
module_init(chrdev_LED_init);
module_exit(chrdev_LED_exit);
//宏定义
MODULE_LICENSE("GPL") //模块的许可证
MODULE_AUTHOR("xy_L") //模块的作者
MODULE_DESCIPTION("chrdev LED module") //模块的描述
MODULE_ALIAS("LED_test") //模块的别名