TA的每日心情 | 怒 2019-11-20 15:22 |
|---|
签到天数: 2 天 [LV.1]初来乍到
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
: i4 g o* q" P; O1 P 前几次我们把linux给boot通了,这所谓的移植也只是个名头而且,真正的移植有时间可以去看看\arch\openrisc下的移植代码,强调我现在也是在用而已,按我的理解的话不是真正意义的开发。; {8 L5 R& R& `* X7 p! s1 ~! C% q, _$ z
& M1 H1 X2 q" ^( ]' S# T2 X 那就继续先用这吧,好,现在回来《or1200软件环境搭建》的过程来,那时我们在虚拟机cross compile了lrz和lsz文件,这是因为什么,因为现在我们只是boot通了内核,除了UART这个外设之外我们在内核或模块程序中有关于ipcores的驱动,所以,我们想在这个最简单的内核上调试程序的时,至少在网卡调通之前我们能用串口下载程序代码,不幸中的万幸了,基于串口的通信协议来传输文件,而lrz和lsz就是串口的z-mode协议的实现。9 @( R/ f! l9 y
& E6 G5 {4 X3 F: A' a, g
好,既然如此,把lrz和lsz扔到openrisc-3.1\arch\openrisc\support\initramfs\bin目录下
) \ B& q- ~5 G! L6 @$ ]. k3 r# h2 N& E2 L7 n
% M$ {1 V5 G3 U
6 b" ^& `% k6 [+ j
回到ipcores上面,在opencores的网站上找到simple_gpio这个工程,下载下来。9 ]* x6 t; z3 Q: ^
. m' }- O& v4 J+ V
+ j. B1 ~( h0 A9 g
- x; Y6 z+ @; |- q' z2 ` 添加到SOC上,按照我自己做的SOC,地址设置为0x910000006 m: L3 O, |' u
) L; R: ^ _* C ~/ H) {; n
; ~- @4 D( C0 f' X
8 W( v' i& U4 t8 }( M( Y9 R 千万千万记得,把板上的LED灯资源绑到GPIO Controller的端口上
& R* a7 F, u# K: h
7 |& D0 g8 l1 M) ]1 P
- O" i5 u3 k8 i1 `( I8 T
1 x' P* Y7 V8 k' G5 {; a
Tcl脚本文件
5 L5 X3 t" N6 r+ ~9 E- |/ r' W& z: l: F9 d9 i4 Z/ n: i! ^
/ _( T6 `" e/ l7 ^
. p$ ]* J! n0 s$ a1 R 现在打开openrisc目录下device-tree文件openrisc-3.1\arch\openrisc\boot\dts,
+ ?# R5 C7 T6 P- @& L
" B) V) \0 U) C# W# r7 L5 F
" N5 ~. f. J' O: l' j q6 r% U; h* K" Y, F( _" w, Y
把最后关于simple_gpio的设备描述加上去/ @) Z) r6 s, `' \- j! D
9 {5 y" d& ^% s
然后,按照《or1200移植linux》的过程重新编译linux源码,生成uImage即可。
/ a# g8 j" O& u1 l6 b8 [" l
% O4 y2 F, d; f# c$ S 重新download uImage启动之后,可在\bin目录下找到lrz和lsz0 Y g- r4 x9 I
" }+ V+ O6 y1 f1 e% A4 P0 _
; x% o' h5 A" R z
( t; f2 d5 y4 m1 A
当输出lrz的时候会弹出传输文件框
" B7 Z. ~7 g+ q) e
: P5 A$ t+ s' Z% T& W7 o
6 S# h* M3 p, B& @) r
+ V0 x$ O% B; V- h, E( f/ d
现在可以随意找个文件测试一下板子的当前设置的baudrate下能不能无错传输。
, k) { d. _ V2 H/ w
4 `, b! U N+ Y4 g, k' _6 C! { 对了,又想起一件事,上一届来公司的应届的学生,来了十几个,也就是我们这批了,有个兄弟前几天跟我说他辞职回桂林了,然后我就问题辞职你要干嘛,他就说回去跟一个小研发团队做项目创业去~估计3月底就撤了~至于什么项目这里就帮他保密一下了,但是我觉得做出来,推得早的话还蛮有市场的,这里也预祝下小朱能升级到朱总,哪天想起我就把我招过去当小弟就够了~呵呵~~~
4 p' Q' ^/ X6 `3 ^/ K
$ n( v7 p" c5 ]1 \ 好,基本步骤就此为止,现在转入到gpio字符驱动代码当中去。7 y+ j e; U& X
8 [) z$ K7 b: D% M% t: K 参考宋宝华老师的《Linux设备驱动开发详解》第6章——字符设备驱动。. F1 T2 L8 w. a3 g% p- u
- l( ^- g1 x9 n2 r: r
8 z! O) g! @8 p V* l7 \- W
6 c* v! m! `1 a 根据我自己的理解总结一下编写字符驱动模块的一般步骤:
1 u. T7 `1 A5 ^1 J, o% v8 S5 z, {2 \6 \/ n: @
1.根据自己编程习惯选择包含cdev的自定义结构体或直接使用cdev结构体。0 o8 v( M: `! s; i
6 F: x" q# N0 g+ l# J) d
2.例化file_operations结构体,然后填充文件操作的有关成员函数,并根据自己要求编写有关成员函数操作。1 A! E6 b& Z3 Z# z9 k
+ t- u" V$ u3 b! c' h& Y 3.编写模块加载函数,包括io资源申请注册,中断号申请注册,设备号申请注册,内存申请,注册字符设备,
/ d, I& T, v4 }
* Z( z: q& }6 y, o 4.编写模块卸装函数,加载模块的逆操作。/ s: A1 S% s& M& e5 h
2 N+ G0 f4 l3 H. W
5.封装成2.6内核的驱动设备模型platform机制,包括编写platform_driver模块加载函数和卸装函数,填充platform_driver结构体的probe,remove,suspend,resume等成员,编写各成员函数。. w1 t8 P6 _/ K' B% V
9 u; X" V" Z) a7 j
6.编写应用层的测试代码。& r3 W) F, D: T$ j
Q8 K/ ^4 @$ z# _
好,到资源栏下载我自己编写的simple gpio controller的字符驱动,对照一面的一般步骤一步步看代码
2 M$ U0 C1 e0 h5 Z3 \( A
" D/ P" b, |& P0 b
+ {! i# e5 a3 X& {) T
) h& ?, U3 C1 I! R( A, I+ o ]' D- {, N 1.cdev结构体,这里我选择编写包含cdev的自定义结构体; j5 M3 J, F1 ^: V3 ^2 ~8 F# G
- X+ `1 u* C% ^ z8 A- struct simple_gpio{
- void __iomem *base;
- struct cdev gpio_cdev;
- };3 p* M$ a/ n1 u* f
5 @* G7 Y$ w5 o F. A* c
) i5 r& E) P+ n9 D# B6 b1 x
2.file_operations结构体
' S8 y4 f) D% E; m4 @, B; J9 I8 r* X3 s6 {' k* [
例化file_operations类,gpio controller operation
2 V- C' v; ^5 a: U7 C$ u- F! {3 ~7 D- }+ x7 z5 ^% p5 \
- 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,
- };4 P+ ?8 D$ A: J7 V, [. Q
3 D, L! d# B8 S# w
, A0 C8 t5 l/ t0 c 编写file_operation成员函数,只实现open和ioctl函数0 o/ T" C8 y3 J( }8 u
' g/ o$ e) Y/ R
- /******************************************* 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;
- }
/ F+ P" S* N% g6 w; J3 C2 ?4 \0 s ) H& Y$ m# o3 o$ s7 v% I
# [, I$ {& p5 T( V9 l0 ~' v9 ]
3.模块加载函数2 Z# q+ I6 c2 v; `
. X& [/ H F, t- [5 G( _$ Q2 ` 这里说明下,函数大部分流程我放在platform driver的probe函数中实现
7 Z* Q. Z( Z7 M2 }0 W" q1 U6 w3 D8 U0 r$ t8 u. b& _ W' D
- /******************************************* 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 K }1 d& L1 |% L P' o! \
- E% S' o. y1 t w- a: { v
$ ?0 X1 N" R7 H8 z5 L6 W
- t" \2 _: Q" R+ {. B& g% k 4.模块卸装函数7 l1 S5 V. J- s# ^7 n1 X
' X3 d* }! R, a$ |
与加载函数相同,流程放在platform driver的remove函数中实现6 C6 f2 @2 }1 t9 u o- z. u
+ b, g& A( g- a& T# A, X- J' @
- /******************************************* 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;
- }
& j! j6 \: `' o 6 P6 p9 x; _) p* s' L1 @
4 O0 Z; `6 b; w! V; \ P+ W) a 5.封装成platform机制
+ b. W5 k: _8 \) F+ i; M' P$ J% D8 d |" h7 E. y6 t$ l
对于这个步骤,基本上是一个固定的格式,个人理解就是通用的字符设备驱动套进去platform机制,至于这个机制,很多blog都有解释,这里就不详细再说明了,主要是platform_device,platform_driver,bus三者之间的关系,platform_driver有一系列的操作函数,platform_device对设备的属性描述。* w7 j1 Y+ y, n$ N
; j8 R, A$ y! O' Z! x
- #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);
- }
' j$ @8 i4 n! h' f% ?3 m; E 6 a" p8 P1 v7 Y7 e [% q) P( h
" ?; h) g# [8 X. x
( j2 V! a) t& m1 j. g 6.测试文件
! g$ H9 \. W- h3 H4 ^% S8 P- H7 `9 h
在linux应用层去做文件打开、读写、关闭操作相信学C的时候就应该有深刻的理解,这里的我们在驱动上没有实现read和write函数的具体操作,只实现了ioctl的操作,所以测试文件很简单,目的是看到LED灯闪烁的现象,所以只是简单打开设备文件,执行在驱动中定义好的命令而已。/ f4 J8 {$ V1 D) @
! o Z( U0 |9 q9 {2 P- //------------------------------------- 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;
- }2 ?! g; Q; h6 _0 a: F
, z3 `% g6 F' T/ U
2 B, y/ P A( Z* H1 Y 至于代码中有少量的注释,或者大家可以自己理解理解,当是自学的过程,主要还是参考宋宝华老师的书,有问题的话留言大家交流交流。 |
|