TA的每日心情 | 怒 2019-11-20 15:22 |
|---|
签到天数: 2 天 [LV.1]初来乍到
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
3 s+ R8 `6 j9 l+ L, Y7 g 前几次我们把linux给boot通了,这所谓的移植也只是个名头而且,真正的移植有时间可以去看看\arch\openrisc下的移植代码,强调我现在也是在用而已,按我的理解的话不是真正意义的开发。5 b( G* g+ P% y" }
3 j( T. q: ?/ J ^- _0 a: f
那就继续先用这吧,好,现在回来《or1200软件环境搭建》的过程来,那时我们在虚拟机cross compile了lrz和lsz文件,这是因为什么,因为现在我们只是boot通了内核,除了UART这个外设之外我们在内核或模块程序中有关于ipcores的驱动,所以,我们想在这个最简单的内核上调试程序的时,至少在网卡调通之前我们能用串口下载程序代码,不幸中的万幸了,基于串口的通信协议来传输文件,而lrz和lsz就是串口的z-mode协议的实现。; k+ ?9 R5 {3 q& X( X+ c& d" _
( A2 ?' i( ^7 Q! _+ ~0 M3 U 好,既然如此,把lrz和lsz扔到openrisc-3.1\arch\openrisc\support\initramfs\bin目录下
$ q8 A, m' K+ G# Z' d3 T0 p3 j! D& [" q8 a I& W# ?. ]
# E9 v, L. X0 p# L1 Q
# {, Y G( s0 F$ k/ t( J5 P& g& ^ 回到ipcores上面,在opencores的网站上找到simple_gpio这个工程,下载下来。
' G) G- M& Q% H/ k! S7 h z& F$ ]; u* T( T( G% m. p
+ f# k/ N) q2 f& Q6 v; ?
+ ?6 v4 E% M9 f! ^$ I5 \
添加到SOC上,按照我自己做的SOC,地址设置为0x91000000
6 c) X u C' r2 L3 O) v6 ~
6 F3 h- O* N$ S- Y) L
6 W# y+ W8 Z9 L
( `* P+ V1 |6 t- O% h, F$ t6 O. v. t2 W: j 千万千万记得,把板上的LED灯资源绑到GPIO Controller的端口上
7 f/ B$ n. E3 G# u8 A5 s9 P+ K, v5 g' e. s
/ V8 R0 Q7 w0 A; W; ]$ H8 z1 |3 `+ W; S1 c# U* A/ \# Q7 Y
Tcl脚本文件2 D3 Z3 T2 [: U& x" @
% q. A [# D- R" C
0 K8 B. k9 [, H: ?; q$ y
# i: [2 ^1 W& e/ @3 J2 y | 现在打开openrisc目录下device-tree文件openrisc-3.1\arch\openrisc\boot\dts,
4 Y$ i2 T; T e$ ^# _) l: {: B9 c+ n8 o7 M& `& d, E! K
$ T" G0 j" K( t2 m" l. C0 n: U
+ D$ H" n, m( y" y' D8 u
把最后关于simple_gpio的设备描述加上去/ ^" H9 \4 h6 G7 M. y0 I+ o0 E
2 B( k( r6 h; H! c 然后,按照《or1200移植linux》的过程重新编译linux源码,生成uImage即可。) D* W9 M- i8 A
# @+ r4 @! O' A1 }/ B6 N3 A
重新download uImage启动之后,可在\bin目录下找到lrz和lsz
: `7 P+ P5 w# ^' b2 j9 p# l. q0 r# B* G" S6 `6 r* ]
5 L. C5 \4 u- V, W& m( v; v8 O& f! e& z+ N) N
当输出lrz的时候会弹出传输文件框
5 W* I( s1 k( ~! u1 t, s9 ?& c- W/ N6 S
4 W5 j8 \, b- _: z: [3 `# e
' Y: K( U0 e- v) y [
现在可以随意找个文件测试一下板子的当前设置的baudrate下能不能无错传输。
% _4 m* @6 K1 K# z( e. B
" w/ S8 D5 S% g _7 g& i. q: F 对了,又想起一件事,上一届来公司的应届的学生,来了十几个,也就是我们这批了,有个兄弟前几天跟我说他辞职回桂林了,然后我就问题辞职你要干嘛,他就说回去跟一个小研发团队做项目创业去~估计3月底就撤了~至于什么项目这里就帮他保密一下了,但是我觉得做出来,推得早的话还蛮有市场的,这里也预祝下小朱能升级到朱总,哪天想起我就把我招过去当小弟就够了~呵呵~~~2 _8 i- b& R6 H9 w% r, W
* r- g2 h* Y$ O4 a 好,基本步骤就此为止,现在转入到gpio字符驱动代码当中去。
1 x" ^8 K1 _: a2 x4 P
+ _& }+ b' P5 {+ i, p+ P' P$ h" c 参考宋宝华老师的《Linux设备驱动开发详解》第6章——字符设备驱动。- C3 T3 }- E" J1 Z
8 R5 u6 f* b6 h* M5 p
2 B; j: p2 `3 s8 X( ?! L0 |! |
3 M5 M+ }& p( c- d. O4 o0 R3 ` 根据我自己的理解总结一下编写字符驱动模块的一般步骤:
5 B) K+ w3 q2 q- E
& o: p2 ]8 z1 W s( b5 r/ E 1.根据自己编程习惯选择包含cdev的自定义结构体或直接使用cdev结构体。+ Q) j8 Q& a. c: C, W- I% z
! B( i6 C% T, l" }% C3 f 2.例化file_operations结构体,然后填充文件操作的有关成员函数,并根据自己要求编写有关成员函数操作。 T. f1 `7 Y% f
0 ~8 J6 B8 t4 X. f: g+ h: U 3.编写模块加载函数,包括io资源申请注册,中断号申请注册,设备号申请注册,内存申请,注册字符设备,
. q( M/ p) j# d2 F) e5 r1 e+ `. s( M2 t6 U( w) g+ z n8 b5 {: j( q
4.编写模块卸装函数,加载模块的逆操作。0 U, c4 P& ]& Y/ i$ ] e& H
3 `% E" _ Z+ J% e" \3 ]# r+ g0 w& P3 a 5.封装成2.6内核的驱动设备模型platform机制,包括编写platform_driver模块加载函数和卸装函数,填充platform_driver结构体的probe,remove,suspend,resume等成员,编写各成员函数。
w. J% s5 x8 o3 e0 Z
/ @6 w* s5 Y6 i& L, n: j0 ` x 6.编写应用层的测试代码。6 j! T6 q4 @: q0 s6 g
6 I w) n3 {5 ~5 `+ a& S$ z' T/ ^' T
好,到资源栏下载我自己编写的simple gpio controller的字符驱动,对照一面的一般步骤一步步看代码. G3 q* [3 l% r! T5 \- T) t
' q/ {9 [4 P8 _$ w4 W; t
1 Z) H7 m# M. u1 E8 t; s% }4 H! p9 Q; U9 R
1.cdev结构体,这里我选择编写包含cdev的自定义结构体' l m" O4 q) V7 b4 l5 o
' ^$ q" s# U8 n4 [! r
- struct simple_gpio{
- void __iomem *base;
- struct cdev gpio_cdev;
- };
H# r( G) {% c! b; \* A( e
6 w6 b( ] m- [9 M" B! j- ~) y
! {( Q. J' U, y v, ]# U B 2.file_operations结构体' U+ }8 g- T/ u. R8 M7 z2 d% E
/ i5 x: L5 a/ g. T+ h- B2 b 例化file_operations类,gpio controller operation
, K5 G. j/ a- g* S/ X8 q
7 C7 q: C5 s4 E5 v- 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,
- };
" S! O6 c9 f1 K4 K) C
0 f9 L- k: A5 Y' B# A
0 j+ {* W+ H: L4 } 编写file_operation成员函数,只实现open和ioctl函数7 E& G1 ^4 c, W# A: H( N- ~# O
6 [, b% \: \! z
- /******************************************* 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;
- }
5 P) s# t: h/ n1 G' a
( \% Z/ q1 D6 {) W4 ~; a! \/ i+ |, ~9 i0 E3 i% b8 { W
3.模块加载函数
! v0 [8 e, \7 G4 N6 |$ |6 [1 T) U6 `
& M$ i" c6 b8 Y6 Y( A4 \ 这里说明下,函数大部分流程我放在platform driver的probe函数中实现" `! R9 B6 o9 g1 R) ~. r' G
+ q+ I$ |. E7 f6 y8 @' p- /******************************************* 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;
- }
1 J" C! s" i! f( T i8 z
7 d$ ]7 v3 h; W6 a, J
, G+ A: R" L0 {- b; C
' m+ I* ^7 m0 k, ?1 ] 4.模块卸装函数$ E# \$ o4 ^; h" g* a3 i! g+ ]
$ [9 h7 E3 s' x/ \ C0 x/ H5 Z 与加载函数相同,流程放在platform driver的remove函数中实现- _3 f- L+ ?+ \" {7 F& c7 M
9 F" ~/ ^4 B3 w1 S1 j0 ?$ W
- /******************************************* 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;
- }
/ `+ m$ }9 R" r
# W$ }/ A: ^( y0 `# I0 }8 i
7 i- x. C J3 B8 a# m) y$ I" B1 p 5.封装成platform机制
- Y* m( u. `( F0 B' ^5 ~. U3 I) l; Y
对于这个步骤,基本上是一个固定的格式,个人理解就是通用的字符设备驱动套进去platform机制,至于这个机制,很多blog都有解释,这里就不详细再说明了,主要是platform_device,platform_driver,bus三者之间的关系,platform_driver有一系列的操作函数,platform_device对设备的属性描述。
' j9 w# H( J* e5 W! X- }: [$ N# `( a5 Q& J/ g
- #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);
- }* s* M$ O3 r( `' f
W1 f9 I# i; T" S0 n+ M
: i6 ~0 {2 p, y; j5 X4 E
4 i1 ^. L: V# C$ E 6.测试文件
% _1 A* L, ~- M7 e. H0 B
( i# f$ Y7 A: u. s 在linux应用层去做文件打开、读写、关闭操作相信学C的时候就应该有深刻的理解,这里的我们在驱动上没有实现read和write函数的具体操作,只实现了ioctl的操作,所以测试文件很简单,目的是看到LED灯闪烁的现象,所以只是简单打开设备文件,执行在驱动中定义好的命令而已。
- ]$ m) I/ W* ^* f9 A2 }& a6 g' N6 U {# K* F7 x: o
- //------------------------------------- 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;
- }- J9 g$ b3 \6 m
4 Z) f5 Z8 l: M( Q
2 U9 r1 h. D' [7 |7 y 至于代码中有少量的注释,或者大家可以自己理解理解,当是自学的过程,主要还是参考宋宝华老师的书,有问题的话留言大家交流交流。 |
|