我本仁慈,奈何苍天不许
分类: LINUX
2013-12-26 16:48:38
ARM接口驱动开发
{//?什么是驱动开发
驱动就是驱使目标设备动作,
而驱动开发就涉及到如何控制设备运行的相关的硬件和软件控制,它是软硬结合的。
而linux驱动开发则涉及到linux为更好的管理设备提出的一些框架模型。
}
{//?如何开发一个新驱动
{//----LED 驱动 (代码见 led)
1. 看电路图 //找到要控制LED灯得管脚
FS_S5PC100_DEV.pdf
FS_S5PC100_CORE.pdf
2. 看芯片手册 //找到LED管脚的控制寄存器的地址
S5PC100_UM_REV1.02.pdf //由GPG3 搜到 GPG3DAT GPG3CON
|--- GPIO口
|--- 串口
|--- SPI口
--------------- |--- I2C口
| CPU | ---------|--- USB口
| s5pc100 | |--- LCD口
| 667MHZ | |--- VGA口
--------------- |--- camera口
| | |--- 音频口
| | |--- SD卡
|-------| |-------- |--- 网口
| DDR | | FLASH |
|-------| ---------
#define S5PC100_GPG3_BASE 0xE0300000
#define S5PC100_LED_CON (S5PC100_GPG3_BASE + 0x01C0)
#define S5PC100_LED_DAT (S5PC100_GPG3_BASE + 0x01C4)
3. 找一个linux驱动的框架
//---driver_char\hello_char\hello.c //这里使用的字符设备驱动的框架
4. 把裸机的驱动整合到linux驱动框架中。
struct led_info{
struct cdev *cdev;
__iomem *led_con;
__iomem *led_dat;
}
led_info *led;
static int hello_open (struct inode *inode, struct file *file)
{
struct resource *res;
led = kmalloc(sizeof(led_info),GFP_KERNEL);
res = request_mem_region(S5PC100_LED_CON, 8, "fsled");
led->led_con = ioremap(S5PC100_LED_CON, 4);
iowrite32(( (ioread32(led->led_con)&(~0x0f))|0x01, led->led_con);
}
5. 在驱动的ioctl 中添加个性的控制 //如 led 的开关控制
6. 修改Makefile 是它能编译后在板子上跑
更改Makefile中
KERNELDIR ?= /root/linux-2.6.35-farsight
为你要编译的内核源码的目录
7. 把编译生成的.ko 文件放到 板子上运行。
insmod hello.ko
lsmod
mknod /dev/hello c 250 0
cat /proc/devices
./test 测试程序 //会看到led灯闪烁
rmmod hello /*
可能报错rmmod: chdir(2.6.35-g9c29226-dirty): No such file or directory
原因是busybox制作时选择简单module配置,会找/lib/modules 让内核导出依赖关系文件
所以rmmod 会坚持依赖关系,这里不重制作busybox了, 直接在/lib/modules 创建 2.6.35-g9c29226-dirty 文件绕过。
*/
{//---led/hello.c 动态映射
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "hello.h"
MODULE_LICENSE ("GPL");
#define DEVICE_MAJOR 250
#define DEVICE_MINOR 0
#define DEVICE_NUM 1
#define DEVICE_NAME "hello"
#define S5PC100_GPG3_BASE 0xE0300000
#define S5PC100_LED_CON (S5PC100_GPG3_BASE + 0x01C0)
#define S5PC100_LED_DAT (S5PC100_GPG3_BASE + 0x01C4)
struct led_info{
struct cdev *cdev;
int __iomem *led_con;
int __iomem *led_dat;
};
struct led_info *led;
static int led_on(void)
{
iowrite32( 1,led->led_dat);
return 0;
}
static int led_off(void)
{
iowrite32( 0,led->led_dat);
return 0;
}
static int hello_open (struct inode *inode, struct file *file)
{
struct resource *res;
/* I/O 内存空间
I/O 内存: I/O 和内存统一编址 如arm powerpc等
I/O 端口: I/O 和内存独立编址 如 x86
---------------------
| request_mem_region | 申请内存区域, 非必须的,但使用它较安全,系统会自动进行互斥处理
----------------------
| ioremap | 动态映射硬件物理地址到内核地址空间
----------------------
| ioread32 iowrite32 | I/O 内存对应的读写函数
----------------------
| iounmap | 解除ioremap的动态映射
----------------------
| release_mem_region | 释放申请的内存区域
----------------------
*/
res = request_mem_region(S5PC100_LED_CON, 8, DEVICE_NAME); /* request_mem_region 申请一个开始于start,len字节的I/O内存区。
申请成功,返回一个非NULL指针;否则返回NULL。
系统当前所有I/O内存分配信息都在/proc/iomem文件中列出,
你分配失败时,可以看看该文件,看谁先占用了该内存区
*/
if(!res)
{
printk("request mem fsled fail\n");
goto err_request_mem;
}
/* ioremap用于动态将I/O内存区映射到内核虚拟地址
phys_addr 为要映射的I/O内存起始地址
size 为要映射的I/O内存的大小,
返回值 为被映射到的虚拟地址
*/
led->led_con = ioremap(S5PC100_LED_CON, 4); //建议先使用request_mem_region后,再ioremap, 较安全
if(!led->led_con)
{
printk("led_con ioremap fail\n");
goto err_map_con;
}
led->led_dat = ioremap(S5PC100_LED_DAT, 4);
if(!led->led_dat)
{
printk("led_dat ioremap fail\n");
goto err_map_dat;
}
iowrite32((ioread32(led->led_con)&(~0x0f))|0x01, led->led_con);
printk ("device opened\n");
return 0;
err_map_dat:
iounmap(led->led_con);
err_map_con:
release_mem_region(S5PC100_LED_CON,8);
err_request_mem:
return -1;
}
static int hello_release (struct inode *inode, struct file *file)
{
iounmap(led->led_dat);
iounmap(led->led_con);
release_mem_region(S5PC100_LED_CON,8);
printk ("device closed\n");
return 0;
}
int hello_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret=0;
switch (cmd) {
case HELLO_ONE:
led_on();
printk ("HELLO_ONE cmd called\n");
break;
case HELLO_TWO:
led_off();
printk ("HELLO_TWO cmd called\n");
break;
default:
printk ("cmd other called\n");
break;
}
return ret;
}
struct file_operations hello_fops = {
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_release,
.ioctl = hello_ioctl
};
static int __init hello_init (void)
{
int ret;
dev_t dev_id = 0;
struct cdev *cdev;
dev_id = MKDEV (DEVICE_MAJOR , DEVICE_MINOR);
ret = register_chrdev_region (dev_id, DEVICE_NUM, DEVICE_NAME);
if (ret<0) {
printk (" can't get major number %d\n", DEVICE_MAJOR);
goto err_reg_cdev;
}
cdev = cdev_alloc();
if(NULL == cdev)
{
printk (" cdev_alloc fail\n");
goto err_cdev_alloc;
}
cdev_init(cdev,&hello_fops);
cdev->owner = THIS_MODULE;
cdev->ops = &hello_fops;
ret = cdev_add(cdev,dev_id,DEVICE_NUM);
if(ret)
{
printk("cdev_add fail %d\n",ret);
goto err_cdev_add;
}
led = kmalloc(sizeof(struct led_info), GFP_KERNEL);
if (!led) {
printk("led: kzmalloc failure\n");
ret = -ENOMEM;
goto err_kmalloc;
}
led->cdev = cdev;
printk("char device register ok\n");
return 0;
err_kmalloc:
err_cdev_add:
cdev_del(cdev);
err_cdev_alloc:
unregister_chrdev_region(dev_id,DEVICE_NUM);
err_reg_cdev:
return ret;
}
static void __exit hello_exit (void)
{
dev_t dev_id = 0;
dev_id = MKDEV (DEVICE_MAJOR , DEVICE_MINOR);
if (led->cdev)
cdev_del (led->cdev);
unregister_chrdev_region (dev_id, DEVICE_NUM);
printk("char unregister ok\n");
}
module_init (hello_init);
module_exit (hello_exit);
}
{//---led_map_desc/hello.c 静态映射
#include
#include
#include
#include
#include
#include
#include
#include
#include "hello.h"
MODULE_LICENSE ("GPL");
#define DEVICE_MAJOR 250
#define DEVICE_MINOR 0
#define DEVICE_NUM 1
#define DEVICE_NAME "hello"
struct cdev *cdev;
static int led_on(void)
{
writel( 0x1, S5PC100_GPG3_BASE + 4);
return 0;
}
static int led_off(void)
{
writel( 0, S5PC100_GPG3_BASE + 4);
return 0;
}
static int hello_open (struct inode *inode, struct file *file)
{
/* S5PC100_GPG3_BASE由来
S5PC100_GPG3_BASE ->S5PC100_GPIO_BASE -> S5P_VA_GPIO -> #define S3C_ADDR_BASE (0xF4000000)
0xF4000000 + 0x00500000 + 0x01C0 + 4 = 0xF45001C4
内核地址静态映射见 arch/arm/mach-s5pc100/mach-smdkc100.c ->smdkc100_map_io -> s5p_iodesc ->
.virtual = (unsigned long)S5P_VA_GPIO, //S5P_VA_GPIO 内核中的地址 0xF4500000
.pfn = __phys_to_pfn(S5P_PA_GPIO), //S5P_PA_GPIO 实际物理地址 对应datasheet中 0xE0300000
0xE0300000 + 01C0 + 4 = 0xE03001C4 对应 datasheet 中 GPG3 实际物理地址
注: 内核启动后会自动调用它,完成静态映射
*/
writel((readl(S5PC100_GPG3_BASE) & ~0xF) | 0x1, S5PC100_GPG3_BASE);
printk ("device opened\n");
return 0;
}
static int hello_release (struct inode *inode, struct file *file)
{
printk ("device closed\n");
return 0;
}
int hello_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret=0;
switch (cmd) {
case HELLO_ONE:
led_on();
printk ("HELLO_ONE cmd called\n");
break;
case HELLO_TWO:
led_off();
printk ("HELLO_TWO cmd called\n");
break;
default:
printk ("cmd other called\n");
break;
}
return ret;
}
struct file_operations hello_fops = {
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_release,
.ioctl = hello_ioctl
};
static int __init hello_init (void)
{
int ret;
dev_t dev_id = 0;
struct cdev *cdev;
dev_id = MKDEV (DEVICE_MAJOR , DEVICE_MINOR);
ret = register_chrdev_region (dev_id, DEVICE_NUM, DEVICE_NAME);
if (ret<0) {
printk (" can't get major number %d\n", DEVICE_MAJOR);
goto err_reg_cdev;
}
cdev = cdev_alloc();
if(NULL == cdev)
{
printk (" cdev_alloc fail\n");
goto err_cdev_alloc;
}
cdev_init(cdev,&hello_fops);
cdev->owner = THIS_MODULE;
cdev->ops = &hello_fops;
ret = cdev_add(cdev,dev_id,DEVICE_NUM);
if(ret)
{
printk("cdev_add fail %d\n",ret);
goto err_cdev_add;
}
printk("char device register ok\n");
return 0;
err_cdev_add:
cdev_del(cdev);
err_cdev_alloc:
unregister_chrdev_region(dev_id,DEVICE_NUM);
err_reg_cdev:
return ret;
}
static void __exit hello_exit (void)
{
dev_t dev_id = 0;
dev_id = MKDEV (DEVICE_MAJOR , DEVICE_MINOR);
if (cdev)
cdev_del (cdev);
unregister_chrdev_region (dev_id, DEVICE_NUM);
printk("char unregister ok\n");
}
module_init (hello_init);
module_exit (hello_exit);
}
}
}
}