OpenRisc-18-or1200下linux简单gpio字符设备驱动
2013-03-22 10:12
337 查看
每周开始上班那一天都特别累,当然,我的宗旨是不上班一定要比上班累~年轻人嘛,多出去走走,所以我现在的工作很轻松,每天想着每周工作两天就够了。
好了,休息五天的第一次,公司没安排事情做就自己瞎搞咯,找后勤阿姨们吹吹水,调杯咖啡喝,上网看看新闻,基本上第一天上午我就是这样浪费的,现在下午的第一天,对了,最近研发新招来了三个研究生,两个是我们学校的师兄,但是对不起也要说一句了,真的好屌丝喔,怎么会沦落来和我这种级别的混了咧,好歹也是研究生们啊,哥······不吐槽了,有时候真蛮无语的,当然实际上现实生活没和他们谈过怎么会来这公司,不过现在和他们两个还蛮聊得来~
好~废话不多说了,前几次我们把linux给boot通了,这所谓的移植也只是个名头而且,真正的移植有时间可以去看看\arch\openrisc下的移植代码,强调我现在也是在用而已,按我的理解的话不是真正意义的开发。
那就继续先用这吧,好,现在回来《or1200软件环境搭建》的过程来,那时我们在虚拟机cross compile了lrz和lsz文件,这是因为什么,因为现在我们只是boot通了内核,除了UART这个外设之外我们在内核或模块程序中有关于ipcores的驱动,所以,我们想在这个最简单的内核上调试程序的时,至少在网卡调通之前我们能用串口下载程序代码,不幸中的万幸了,基于串口的通信协议来传输文件,而lrz和lsz就是串口的z-mode协议的实现。
好,既然如此,把lrz和lsz扔到openrisc-3.1\arch\openrisc\support\initramfs\bin目录下
回到ipcores上面,在opencores的网站上找到simple_gpio这个工程,下载下来。
添加到SOC上,按照我自己做的SOC,地址设置为0x91000000
千万千万记得,把板上的LED灯资源绑到GPIO Controller的端口上
Tcl脚本文件
现在打开openrisc目录下device-tree文件openrisc-3.1\arch\openrisc\boot\dts,
把最后关于simple_gpio的设备描述加上去
然后,按照《or1200移植linux》的过程重新编译linux源码,生成uImage即可。
重新download uImage启动之后,可在\bin目录下找到lrz和lsz
当输出lrz的时候会弹出传输文件框
现在可以随意找个文件测试一下板子的当前设置的baudrate下能不能无错传输。
对了,又想起一件事,上一届来公司的应届的学生,来了十几个,也就是我们这批了,有个兄弟前几天跟我说他辞职回桂林了,然后我就问题辞职你要干嘛,他就说回去跟一个小研发团队做项目创业去~估计3月底就撤了~至于什么项目这里就帮他保密一下了,但是我觉得做出来,推得早的话还蛮有市场的,这里也预祝下小朱能升级到朱总,哪天想起我就把我招过去当小弟就够了~呵呵~~~
好,基本步骤就此为止,现在转入到gpio字符驱动代码当中去。
参考宋宝华老师的《Linux设备驱动开发详解》第6章——字符设备驱动。
根据我自己的理解总结一下编写字符驱动模块的一般步骤:
1.根据自己编程习惯选择包含cdev的自定义结构体或直接使用cdev结构体。
2.例化file_operations结构体,然后填充文件操作的有关成员函数,并根据自己要求编写有关成员函数操作。
3.编写模块加载函数,包括io资源申请注册,中断号申请注册,设备号申请注册,内存申请,注册字符设备,
4.编写模块卸装函数,加载模块的逆操作。
5.封装成2.6内核的驱动设备模型platform机制,包括编写platform_driver模块加载函数和卸装函数,填充platform_driver结构体的probe,remove,suspend,resume等成员,编写各成员函数。
6.编写应用层的测试代码。
好,到资源栏下载我自己编写的simple gpio controller的字符驱动,对照一面的一般步骤一步步看代码
1.cdev结构体,这里我选择编写包含cdev的自定义结构体
[cpp]
view plaincopyprint?
struct simple_gpio{
void __iomem *base;
struct cdev gpio_cdev;
};
2.file_operations结构体
例化file_operations类,gpio controller operation
[cpp]
view plaincopyprint?
struct file_operations gpio_ctl_ops = {
.owner = THIS_MODULE,
.read = gpio_read,
.write = gpio_write,
.unlocked_ioctl = gpio_ioctl,
.open = gpio_open,
.release = gpio_release,
};
编写file_operation成员函数,只实现open和ioctl函数
[cpp]
view plaincopyprint?
/******************************************* for file operations *******************************************/
int gpio_open(struct inode *inode, struct file *file){
struct simple_gpio *gpio;
gpio = container_of(inode->i_cdev, struct simple_gpio, gpio_cdev);
file->private_data = gpio;
return 0;
}
int gpio_release(struct inode *inode, struct file *file){
return 0;
}
ssize_t gpio_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops){
return count;
}
ssize_t gpio_write(struct file *file, const char __user *buf, size_t count, loff_t *f_ops){
return count;
}
long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long data){
struct simple_gpio *gpio = file->private_data;
switch(cmd){
case LED_ON:
simple_gpio_write8(gpio, SIMPLE_GPIO_DAT, 0x00);
break;
case LED_OFF:
simple_gpio_write8(gpio, SIMPLE_GPIO_DAT, 0xff);
break;
default:
printk(KERN_ALERT"led control : no cmd run [ --kernel-- ]\n");
return (-EINVAL);
}
return 0;
}
3.模块加载函数
这里说明下,函数大部分流程我放在platform driver的probe函数中实现
[cpp]
view plaincopyprint?
/******************************************* for char device driver *******************************************/
static int __devinit simple_gpio_setup(struct simple_gpio *gpio){
cdev_init(&gpio->gpio_cdev, &gpio_ctl_ops);
gpio->gpio_cdev.owner = THIS_MODULE;
register_chrdev_region(MKDEV(GPIO_MAJOR, GPIO_MINOR), 1, "simple_gpio");
return cdev_add(&gpio->gpio_cdev, MKDEV(GPIO_MAJOR, GPIO_MINOR), 1);
}
/******************************************* for platform device driver *******************************************/
static int __devinit simple_gpio_probe(struct platform_device *pdev){
int ret;
struct simple_gpio *gpio;
struct resource *io_res, *irq_res;
/* get resources info*/
io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!io_res)
return -ENODEV;
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq_res)
return -ENODEV;
/* request memery for simple_gpio */
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
if (!gpio)
return -ENOMEM;
if (!devm_request_mem_region(&pdev->dev, io_res->start,
resource_size(io_res), pdev->name)){
dev_err(&pdev->dev, "Memory region busy\n");
return -EBUSY;
}
/* map io memery to kenel space */
gpio->base = devm_ioremap_nocache(&pdev->dev, io_res->start,
resource_size(io_res));
if (!gpio->base){
dev_err(&pdev->dev, "Unable to map registers\n");
return -EIO;
}
/* register simple_gpio char device */
simple_gpio_setup(gpio);
/* set outputs and light leds */
simple_gpio_led_init(gpio);
/* register interrupt */
ret = devm_request_irq(&pdev->dev, irq_res->start, simple_gpio_isr, 0,
pdev->name, gpio);
if(ret){
dev_err(&pdev->dev, "Cannot claim IRQ\n");
return ret;
}
/* save struct gpio as device private data */
platform_set_drvdata(pdev, gpio);
/* mount into sysfs */
gpio_class = class_create(THIS_MODULE, "gpio_class");
device_create(gpio_class, NULL, MKDEV(GPIO_MAJOR, GPIO_MINOR), NULL, "led");
return 0;
}
4.模块卸装函数
与加载函数相同,流程放在platform driver的remove函数中实现
[cpp]
view plaincopyprint?
/******************************************* for char device driver *******************************************/
static void __devexit simple_gpio_clean(struct simple_gpio *gpio){
unregister_chrdev_region(MKDEV(GPIO_MAJOR, GPIO_MINOR), 1);
cdev_del(&gpio->gpio_cdev);
}
/******************************************* for platform device driver *******************************************/
static int __devexit simple_gpio_remove(struct platform_device* pdev){
struct simple_gpio *gpio = platform_get_drvdata(pdev);
/* extinguish leds */
simple_gpio_led_exit(gpio);
/* remove data */
platform_set_drvdata(pdev, NULL);
/* unregister simple_gpio char device */
simple_gpio_clean(gpio);
device_destroy(gpio_class, MKDEV(GPIO_MAJOR, GPIO_MINOR));
class_destroy(gpio_class);
return 0;
}
5.封装成platform机制
对于这个步骤,基本上是一个固定的格式,个人理解就是通用的字符设备驱动套进去platform机制,至于这个机制,很多blog都有解释,这里就不详细再说明了,主要是platform_device,platform_driver,bus三者之间的关系,platform_driver有一系列的操作函数,platform_device对设备的属性描述。
[cpp]
view plaincopyprint?
#define simple_gpio_suspend NULL
#define simple_gpio_resume NULL
static struct of_device_id simple_gpio_match[] = {
{ .compatible = "opencores,simple_gpio", },
{},
};
MODULE_DEVICE_TABLE(of, simple_gpio_match);
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:simple_gpio");
static struct platform_driver simple_gpio_driver = {
.probe = simple_gpio_probe,
.remove = __devexit_p(simple_gpio_remove),
.suspend = simple_gpio_suspend,
.resume = simple_gpio_resume,
.driver = {
.owner = THIS_MODULE,
.name = "simple_gpio",
.of_match_table = simple_gpio_match,
},
};
static int __init simple_gpio_init(void){
return platform_driver_register(&simple_gpio_driver);
}
static void __exit simple_gpio_exit(void){
platform_driver_unregister(&simple_gpio_driver);
}
6.测试文件
在linux应用层去做文件打开、读写、关闭操作相信学C的时候就应该有深刻的理解,这里的我们在驱动上没有实现read和write函数的具体操作,只实现了ioctl的操作,所以测试文件很简单,目的是看到LED灯闪烁的现象,所以只是简单打开设备文件,执行在驱动中定义好的命令而已。
[cpp]
view plaincopyprint?
//------------------------------------- main ---------------------------------------------
int main(void)
{
int fd;
int ret;
char *i;
printf("\nstart simple_gpio_led_driver test ! \n\n");
sleep(1);
fd = open(DEVICE_NAME, O_RDWR);
printf("fd = %d\n",fd);
if (fd == -1){
printf("open device %s error !\n",DEVICE_NAME);
}
else{
while(1){
ioctl(fd,LED_OFF);
printf ("leds is off ! \n");
sleep(1);//sleep for 1s
ioctl(fd,LED_ON);
printf ("leds is on ! \n");
sleep(1);
}
// close
ret = close(fd);
printf ("ret=%d\n",ret);
printf ("close gpio_led_driver test\n");
}
return 0;
}
至于代码中有少量的注释,或者大家可以自己理解理解,当是自学的过程,主要还是参考宋宝华老师的书,有问题的话留言大家交流交流。
现在把simple_gpio目录挂到虚拟机里,然后在terminal中make,得到设备驱动模块文件
然后在terminal中编译simple_led.c文件
or32-linux-gcc -o simple_led simple_led.c
最后需要的是两个文件,simple_gpio_driver.ko,simple_led。
提取出来,然后在SecureCRT中输入lrz
将这两个文件添加到发送列表中,然后确定发送,
然后更改simple_led的文件属性
然后就可以加载module和运行测试代码
如果顺利的话,在加载模块的同时板子的上LEDs会同时light up。
对应加载模块后的内核信息
用modinfo命令可以看到在编写字符驱动时的模块信息
然后运行测试代码
在运行simple_led后,板子上LED 不断light up or down,同时在控制台上到有相应的提示信息输出。
到此,关于GPIO Controller的字符设备驱动就介绍到这里,如果目前想在驱动方面打发打发时间的话就可以啃相关的学习书籍······所以说目前我在公司就是这样打发时间的了~谁叫公司就养我在这又不给活我干咧~加油吧~
下一次写点什么也不知道了,好像还研究了frame buff和I2C一段时间,下次看看有没有能力总结经验出来,现在还是努力地先学,目前在看txj老师录的关于RAW-OS的培训视频,学,找回激情,保持激情,
好了,休息五天的第一次,公司没安排事情做就自己瞎搞咯,找后勤阿姨们吹吹水,调杯咖啡喝,上网看看新闻,基本上第一天上午我就是这样浪费的,现在下午的第一天,对了,最近研发新招来了三个研究生,两个是我们学校的师兄,但是对不起也要说一句了,真的好屌丝喔,怎么会沦落来和我这种级别的混了咧,好歹也是研究生们啊,哥······不吐槽了,有时候真蛮无语的,当然实际上现实生活没和他们谈过怎么会来这公司,不过现在和他们两个还蛮聊得来~
好~废话不多说了,前几次我们把linux给boot通了,这所谓的移植也只是个名头而且,真正的移植有时间可以去看看\arch\openrisc下的移植代码,强调我现在也是在用而已,按我的理解的话不是真正意义的开发。
那就继续先用这吧,好,现在回来《or1200软件环境搭建》的过程来,那时我们在虚拟机cross compile了lrz和lsz文件,这是因为什么,因为现在我们只是boot通了内核,除了UART这个外设之外我们在内核或模块程序中有关于ipcores的驱动,所以,我们想在这个最简单的内核上调试程序的时,至少在网卡调通之前我们能用串口下载程序代码,不幸中的万幸了,基于串口的通信协议来传输文件,而lrz和lsz就是串口的z-mode协议的实现。
好,既然如此,把lrz和lsz扔到openrisc-3.1\arch\openrisc\support\initramfs\bin目录下
回到ipcores上面,在opencores的网站上找到simple_gpio这个工程,下载下来。
添加到SOC上,按照我自己做的SOC,地址设置为0x91000000
千万千万记得,把板上的LED灯资源绑到GPIO Controller的端口上
Tcl脚本文件
现在打开openrisc目录下device-tree文件openrisc-3.1\arch\openrisc\boot\dts,
把最后关于simple_gpio的设备描述加上去
然后,按照《or1200移植linux》的过程重新编译linux源码,生成uImage即可。
重新download uImage启动之后,可在\bin目录下找到lrz和lsz
当输出lrz的时候会弹出传输文件框
现在可以随意找个文件测试一下板子的当前设置的baudrate下能不能无错传输。
对了,又想起一件事,上一届来公司的应届的学生,来了十几个,也就是我们这批了,有个兄弟前几天跟我说他辞职回桂林了,然后我就问题辞职你要干嘛,他就说回去跟一个小研发团队做项目创业去~估计3月底就撤了~至于什么项目这里就帮他保密一下了,但是我觉得做出来,推得早的话还蛮有市场的,这里也预祝下小朱能升级到朱总,哪天想起我就把我招过去当小弟就够了~呵呵~~~
好,基本步骤就此为止,现在转入到gpio字符驱动代码当中去。
参考宋宝华老师的《Linux设备驱动开发详解》第6章——字符设备驱动。
根据我自己的理解总结一下编写字符驱动模块的一般步骤:
1.根据自己编程习惯选择包含cdev的自定义结构体或直接使用cdev结构体。
2.例化file_operations结构体,然后填充文件操作的有关成员函数,并根据自己要求编写有关成员函数操作。
3.编写模块加载函数,包括io资源申请注册,中断号申请注册,设备号申请注册,内存申请,注册字符设备,
4.编写模块卸装函数,加载模块的逆操作。
5.封装成2.6内核的驱动设备模型platform机制,包括编写platform_driver模块加载函数和卸装函数,填充platform_driver结构体的probe,remove,suspend,resume等成员,编写各成员函数。
6.编写应用层的测试代码。
好,到资源栏下载我自己编写的simple gpio controller的字符驱动,对照一面的一般步骤一步步看代码
1.cdev结构体,这里我选择编写包含cdev的自定义结构体
[cpp]
view plaincopyprint?
struct simple_gpio{
void __iomem *base;
struct cdev gpio_cdev;
};
struct simple_gpio{ void __iomem *base; struct cdev gpio_cdev; };
2.file_operations结构体
例化file_operations类,gpio controller operation
[cpp]
view plaincopyprint?
struct file_operations gpio_ctl_ops = {
.owner = THIS_MODULE,
.read = gpio_read,
.write = gpio_write,
.unlocked_ioctl = gpio_ioctl,
.open = gpio_open,
.release = gpio_release,
};
struct file_operations gpio_ctl_ops = { .owner = THIS_MODULE, .read = gpio_read, .write = gpio_write, .unlocked_ioctl = gpio_ioctl, .open = gpio_open, .release = gpio_release, };
编写file_operation成员函数,只实现open和ioctl函数
[cpp]
view plaincopyprint?
/******************************************* for file operations *******************************************/
int gpio_open(struct inode *inode, struct file *file){
struct simple_gpio *gpio;
gpio = container_of(inode->i_cdev, struct simple_gpio, gpio_cdev);
file->private_data = gpio;
return 0;
}
int gpio_release(struct inode *inode, struct file *file){
return 0;
}
ssize_t gpio_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops){
return count;
}
ssize_t gpio_write(struct file *file, const char __user *buf, size_t count, loff_t *f_ops){
return count;
}
long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long data){
struct simple_gpio *gpio = file->private_data;
switch(cmd){
case LED_ON:
simple_gpio_write8(gpio, SIMPLE_GPIO_DAT, 0x00);
break;
case LED_OFF:
simple_gpio_write8(gpio, SIMPLE_GPIO_DAT, 0xff);
break;
default:
printk(KERN_ALERT"led control : no cmd run [ --kernel-- ]\n");
return (-EINVAL);
}
return 0;
}
/******************************************* for file operations *******************************************/ int gpio_open(struct inode *inode, struct file *file){ struct simple_gpio *gpio; gpio = container_of(inode->i_cdev, struct simple_gpio, gpio_cdev); file->private_data = gpio; return 0; } int gpio_release(struct inode *inode, struct file *file){ return 0; } ssize_t gpio_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops){ return count; } ssize_t gpio_write(struct file *file, const char __user *buf, size_t count, loff_t *f_ops){ return count; } long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long data){ struct simple_gpio *gpio = file->private_data; switch(cmd){ case LED_ON: simple_gpio_write8(gpio, SIMPLE_GPIO_DAT, 0x00); break; case LED_OFF: simple_gpio_write8(gpio, SIMPLE_GPIO_DAT, 0xff); break; default: printk(KERN_ALERT"led control : no cmd run [ --kernel-- ]\n"); return (-EINVAL); } return 0; }
3.模块加载函数
这里说明下,函数大部分流程我放在platform driver的probe函数中实现
[cpp]
view plaincopyprint?
/******************************************* for char device driver *******************************************/
static int __devinit simple_gpio_setup(struct simple_gpio *gpio){
cdev_init(&gpio->gpio_cdev, &gpio_ctl_ops);
gpio->gpio_cdev.owner = THIS_MODULE;
register_chrdev_region(MKDEV(GPIO_MAJOR, GPIO_MINOR), 1, "simple_gpio");
return cdev_add(&gpio->gpio_cdev, MKDEV(GPIO_MAJOR, GPIO_MINOR), 1);
}
/******************************************* for platform device driver *******************************************/
static int __devinit simple_gpio_probe(struct platform_device *pdev){
int ret;
struct simple_gpio *gpio;
struct resource *io_res, *irq_res;
/* get resources info*/
io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!io_res)
return -ENODEV;
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq_res)
return -ENODEV;
/* request memery for simple_gpio */
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
if (!gpio)
return -ENOMEM;
if (!devm_request_mem_region(&pdev->dev, io_res->start,
resource_size(io_res), pdev->name)){
dev_err(&pdev->dev, "Memory region busy\n");
return -EBUSY;
}
/* map io memery to kenel space */
gpio->base = devm_ioremap_nocache(&pdev->dev, io_res->start,
resource_size(io_res));
if (!gpio->base){
dev_err(&pdev->dev, "Unable to map registers\n");
return -EIO;
}
/* register simple_gpio char device */
simple_gpio_setup(gpio);
/* set outputs and light leds */
simple_gpio_led_init(gpio);
/* register interrupt */
ret = devm_request_irq(&pdev->dev, irq_res->start, simple_gpio_isr, 0,
pdev->name, gpio);
if(ret){
dev_err(&pdev->dev, "Cannot claim IRQ\n");
return ret;
}
/* save struct gpio as device private data */
platform_set_drvdata(pdev, gpio);
/* mount into sysfs */
gpio_class = class_create(THIS_MODULE, "gpio_class");
device_create(gpio_class, NULL, MKDEV(GPIO_MAJOR, GPIO_MINOR), NULL, "led");
return 0;
}
/******************************************* for char device driver *******************************************/ static int __devinit simple_gpio_setup(struct simple_gpio *gpio){ cdev_init(&gpio->gpio_cdev, &gpio_ctl_ops); gpio->gpio_cdev.owner = THIS_MODULE; register_chrdev_region(MKDEV(GPIO_MAJOR, GPIO_MINOR), 1, "simple_gpio"); return cdev_add(&gpio->gpio_cdev, MKDEV(GPIO_MAJOR, GPIO_MINOR), 1); } /******************************************* for platform device driver *******************************************/ static int __devinit simple_gpio_probe(struct platform_device *pdev){ int ret; struct simple_gpio *gpio; struct resource *io_res, *irq_res; /* get resources info*/ io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!io_res) return -ENODEV; irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!irq_res) return -ENODEV; /* request memery for simple_gpio */ gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); if (!gpio) return -ENOMEM; if (!devm_request_mem_region(&pdev->dev, io_res->start, resource_size(io_res), pdev->name)){ dev_err(&pdev->dev, "Memory region busy\n"); return -EBUSY; } /* map io memery to kenel space */ gpio->base = devm_ioremap_nocache(&pdev->dev, io_res->start, resource_size(io_res)); if (!gpio->base){ dev_err(&pdev->dev, "Unable to map registers\n"); return -EIO; } /* register simple_gpio char device */ simple_gpio_setup(gpio); /* set outputs and light leds */ simple_gpio_led_init(gpio); /* register interrupt */ ret = devm_request_irq(&pdev->dev, irq_res->start, simple_gpio_isr, 0, pdev->name, gpio); if(ret){ dev_err(&pdev->dev, "Cannot claim IRQ\n"); return ret; } /* save struct gpio as device private data */ platform_set_drvdata(pdev, gpio); /* mount into sysfs */ gpio_class = class_create(THIS_MODULE, "gpio_class"); device_create(gpio_class, NULL, MKDEV(GPIO_MAJOR, GPIO_MINOR), NULL, "led"); return 0; }
4.模块卸装函数
与加载函数相同,流程放在platform driver的remove函数中实现
[cpp]
view plaincopyprint?
/******************************************* for char device driver *******************************************/
static void __devexit simple_gpio_clean(struct simple_gpio *gpio){
unregister_chrdev_region(MKDEV(GPIO_MAJOR, GPIO_MINOR), 1);
cdev_del(&gpio->gpio_cdev);
}
/******************************************* for platform device driver *******************************************/
static int __devexit simple_gpio_remove(struct platform_device* pdev){
struct simple_gpio *gpio = platform_get_drvdata(pdev);
/* extinguish leds */
simple_gpio_led_exit(gpio);
/* remove data */
platform_set_drvdata(pdev, NULL);
/* unregister simple_gpio char device */
simple_gpio_clean(gpio);
device_destroy(gpio_class, MKDEV(GPIO_MAJOR, GPIO_MINOR));
class_destroy(gpio_class);
return 0;
}
/******************************************* for char device driver *******************************************/ static void __devexit simple_gpio_clean(struct simple_gpio *gpio){ unregister_chrdev_region(MKDEV(GPIO_MAJOR, GPIO_MINOR), 1); cdev_del(&gpio->gpio_cdev); } /******************************************* for platform device driver *******************************************/ static int __devexit simple_gpio_remove(struct platform_device* pdev){ struct simple_gpio *gpio = platform_get_drvdata(pdev); /* extinguish leds */ simple_gpio_led_exit(gpio); /* remove data */ platform_set_drvdata(pdev, NULL); /* unregister simple_gpio char device */ simple_gpio_clean(gpio); device_destroy(gpio_class, MKDEV(GPIO_MAJOR, GPIO_MINOR)); class_destroy(gpio_class); return 0; }
5.封装成platform机制
对于这个步骤,基本上是一个固定的格式,个人理解就是通用的字符设备驱动套进去platform机制,至于这个机制,很多blog都有解释,这里就不详细再说明了,主要是platform_device,platform_driver,bus三者之间的关系,platform_driver有一系列的操作函数,platform_device对设备的属性描述。
[cpp]
view plaincopyprint?
#define simple_gpio_suspend NULL
#define simple_gpio_resume NULL
static struct of_device_id simple_gpio_match[] = {
{ .compatible = "opencores,simple_gpio", },
{},
};
MODULE_DEVICE_TABLE(of, simple_gpio_match);
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:simple_gpio");
static struct platform_driver simple_gpio_driver = {
.probe = simple_gpio_probe,
.remove = __devexit_p(simple_gpio_remove),
.suspend = simple_gpio_suspend,
.resume = simple_gpio_resume,
.driver = {
.owner = THIS_MODULE,
.name = "simple_gpio",
.of_match_table = simple_gpio_match,
},
};
static int __init simple_gpio_init(void){
return platform_driver_register(&simple_gpio_driver);
}
static void __exit simple_gpio_exit(void){
platform_driver_unregister(&simple_gpio_driver);
}
#define simple_gpio_suspend NULL #define simple_gpio_resume NULL static struct of_device_id simple_gpio_match[] = { { .compatible = "opencores,simple_gpio", }, {}, }; MODULE_DEVICE_TABLE(of, simple_gpio_match); /* work with hotplug and coldplug */ MODULE_ALIAS("platform:simple_gpio"); static struct platform_driver simple_gpio_driver = { .probe = simple_gpio_probe, .remove = __devexit_p(simple_gpio_remove), .suspend = simple_gpio_suspend, .resume = simple_gpio_resume, .driver = { .owner = THIS_MODULE, .name = "simple_gpio", .of_match_table = simple_gpio_match, }, }; static int __init simple_gpio_init(void){ return platform_driver_register(&simple_gpio_driver); } static void __exit simple_gpio_exit(void){ platform_driver_unregister(&simple_gpio_driver); }
6.测试文件
在linux应用层去做文件打开、读写、关闭操作相信学C的时候就应该有深刻的理解,这里的我们在驱动上没有实现read和write函数的具体操作,只实现了ioctl的操作,所以测试文件很简单,目的是看到LED灯闪烁的现象,所以只是简单打开设备文件,执行在驱动中定义好的命令而已。
[cpp]
view plaincopyprint?
//------------------------------------- main ---------------------------------------------
int main(void)
{
int fd;
int ret;
char *i;
printf("\nstart simple_gpio_led_driver test ! \n\n");
sleep(1);
fd = open(DEVICE_NAME, O_RDWR);
printf("fd = %d\n",fd);
if (fd == -1){
printf("open device %s error !\n",DEVICE_NAME);
}
else{
while(1){
ioctl(fd,LED_OFF);
printf ("leds is off ! \n");
sleep(1);//sleep for 1s
ioctl(fd,LED_ON);
printf ("leds is on ! \n");
sleep(1);
}
// close
ret = close(fd);
printf ("ret=%d\n",ret);
printf ("close gpio_led_driver test\n");
}
return 0;
}
//------------------------------------- main --------------------------------------------- int main(void) { int fd; int ret; char *i; printf("\nstart simple_gpio_led_driver test ! \n\n"); sleep(1); fd = open(DEVICE_NAME, O_RDWR); printf("fd = %d\n",fd); if (fd == -1){ printf("open device %s error !\n",DEVICE_NAME); } else{ while(1){ ioctl(fd,LED_OFF); printf ("leds is off ! \n"); sleep(1);//sleep for 1s ioctl(fd,LED_ON); printf ("leds is on ! \n"); sleep(1); } // close ret = close(fd); printf ("ret=%d\n",ret); printf ("close gpio_led_driver test\n"); } return 0; }
至于代码中有少量的注释,或者大家可以自己理解理解,当是自学的过程,主要还是参考宋宝华老师的书,有问题的话留言大家交流交流。
现在把simple_gpio目录挂到虚拟机里,然后在terminal中make,得到设备驱动模块文件
然后在terminal中编译simple_led.c文件
or32-linux-gcc -o simple_led simple_led.c
最后需要的是两个文件,simple_gpio_driver.ko,simple_led。
提取出来,然后在SecureCRT中输入lrz
将这两个文件添加到发送列表中,然后确定发送,
然后更改simple_led的文件属性
然后就可以加载module和运行测试代码
如果顺利的话,在加载模块的同时板子的上LEDs会同时light up。
对应加载模块后的内核信息
用modinfo命令可以看到在编写字符驱动时的模块信息
然后运行测试代码
在运行simple_led后,板子上LED 不断light up or down,同时在控制台上到有相应的提示信息输出。
到此,关于GPIO Controller的字符设备驱动就介绍到这里,如果目前想在驱动方面打发打发时间的话就可以啃相关的学习书籍······所以说目前我在公司就是这样打发时间的了~谁叫公司就养我在这又不给活我干咧~加油吧~
下一次写点什么也不知道了,好像还研究了frame buff和I2C一段时间,下次看看有没有能力总结经验出来,现在还是努力地先学,目前在看txj老师录的关于RAW-OS的培训视频,学,找回激情,保持激情,
相关文章推荐
- OpenRisc-18-or1200下linux简单gpio字符设备驱动
- or1200下linux简单gpio字符设备驱动
- OpenRisc-19-or1200下linux的i2c(一)
- OpenRisc-20-or1200下linux的i2c(二)
- OpenRisc-19-or1200下linux的i2c(一)
- OpenRisc-20-or1200下linux的i2c(二)
- linux SPI驱动——简单的gpio模拟SPI驱动测试 (二)
- or1200下linux的i2c(二)
- [gpio]Linux GPIO简单使用方式1-sysfs
- linux c fork简单调用bin or shell script demo
- or1200下linux的i2c(一)
- [gpio]Linux GPIO简单使用方式2-sysfs
- or1200下linux的i2c(二)
- or1200下linux的i2c(一)
- Linux简单设备驱动(1):使用IO内存操作GPIO--LED
- linux or mac 简单定制命令(svn 批量处理命令)
- Linux下用文件IO的方式操作GPIO(/sys/class/gpio)简单易操作
- linux下oracle自启动,oratab/dbstart等脚本的配置
- 一个初学者对Linux的简单认识
- linux设备驱动归纳总结(十二):简单的数码相框