找回密码
 注册
关于网站域名变更的通知
查看: 515|回复: 3
打印 上一主题 下一主题

linux学习之路_or1200下linux简单gpio字符设备驱动(上)

[复制链接]
  • TA的每日心情

    2019-11-20 15:22
  • 签到天数: 2 天

    [LV.1]初来乍到

    跳转到指定楼层
    1#
    发表于 2021-7-26 13:28 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

    EDA365欢迎您登录!

    您需要 登录 才可以下载或查看,没有帐号?注册

    x

    ; O9 B5 Z; E$ N( q5 M        前几次我们把linux给boot通了,这所谓的移植也只是个名头而且,真正的移植有时间可以去看看\arch\openrisc下的移植代码,强调我现在也是在用而已,按我的理解的话不是真正意义的开发。
    - {3 B+ v% x, N7 T4 _, V3 m0 K6 B$ f. b/ j- a
            那就继续先用这吧,好,现在回来《or1200软件环境搭建》的过程来,那时我们在虚拟机cross compile了lrz和lsz文件,这是因为什么,因为现在我们只是boot通了内核,除了UART这个外设之外我们在内核或模块程序中有关于ipcores的驱动,所以,我们想在这个最简单的内核上调试程序的时,至少在网卡调通之前我们能用串口下载程序代码,不幸中的万幸了,基于串口的通信协议来传输文件,而lrz和lsz就是串口的z-mode协议的实现。
    : E. I1 \" K" b
    ( n% k7 \. y( D" H8 O        好,既然如此,把lrz和lsz扔到openrisc-3.1\arch\openrisc\support\initramfs\bin目录下& C: g* A* M% _- R( N/ o" I" x8 \

    : j% V# V  v* R) Z4 q
    - L% E' C" f' _; @' u
    8 c) Z- Y% B$ a8 L        回到ipcores上面,在opencores的网站上找到simple_gpio这个工程,下载下来。7 r4 |/ O; U5 e2 o5 M

    . E* z2 \0 N( H2 r6 G - k0 L4 s4 P' M9 E5 e
    ' F& p6 P+ b* L+ o
            添加到SOC上,按照我自己做的SOC,地址设置为0x91000000% z' Z2 Y/ [+ i

    % p, L$ N8 Y) B- G1 P- t   p" k" \8 Z1 u3 N7 H+ S

    . p3 f9 w' V7 A        千万千万记得,把板上的LED灯资源绑到GPIO Controller的端口上+ e  _- _3 \( O4 V2 f
      F5 X0 f, T5 j) t* s
    3 _) T7 z$ ?% A8 J9 ^$ q4 T
    * z% b9 p) O! B: |  m- m+ m
            Tcl脚本文件
    & j; N$ p  `) a9 ?6 ^+ |
    9 g$ {! r: Y5 ]/ W( K, v, C' g! x: U6 C7 m
    4 u. T% M* j: {( K1 z$ a7 n; c0 R/ J9 @! C4 B: Q6 N9 B1 t
            现在打开openrisc目录下device-tree文件openrisc-3.1\arch\openrisc\boot\dts,' b0 D  r, K& k+ e8 O
    ' O5 k/ w" e: A* x
    ( q! K+ J( u  H  B* q

    " L% J) ~* L( |6 Z3 A) e        把最后关于simple_gpio的设备描述加上去
    2 N* ?% Q) C, c% j% X2 U+ Z3 G+ ]8 \* r: c( ?! ]9 O3 A1 x
            然后,按照《or1200移植linux》的过程重新编译linux源码,生成uImage即可。' @, P3 `+ t3 `1 q. i6 b
    7 d) V  W! o1 L( A, o$ ~
            重新download uImage启动之后,可在\bin目录下找到lrz和lsz9 V% K; v5 `* d6 V2 i; N) f$ v
    & \+ e/ I) a+ _1 x1 H

    ' U) b& k/ L( z0 t
    * O# [( m1 @/ [9 ~, o! h        当输出lrz的时候会弹出传输文件框
    - `1 ~$ d. t( B2 `, y5 |1 Y( q- Q  f( k
    5 A% X: w3 b1 G. u7 ~- a/ a9 M
    $ ?: T3 m+ z* V. T
            现在可以随意找个文件测试一下板子的当前设置的baudrate下能不能无错传输。6 v5 r# K7 C( v, s2 B; y" [3 z' S8 N
    8 N1 y1 I0 h- u
            对了,又想起一件事,上一届来公司的应届的学生,来了十几个,也就是我们这批了,有个兄弟前几天跟我说他辞职回桂林了,然后我就问题辞职你要干嘛,他就说回去跟一个小研发团队做项目创业去~估计3月底就撤了~至于什么项目这里就帮他保密一下了,但是我觉得做出来,推得早的话还蛮有市场的,这里也预祝下小朱能升级到朱总,哪天想起我就把我招过去当小弟就够了~呵呵~~~, E- h" T+ x) F$ X' _: C
    0 h, u7 ~+ `( q0 c7 n+ R, q& t
            好,基本步骤就此为止,现在转入到gpio字符驱动代码当中去。
    , C3 M, r( r* F1 C8 U
    * D4 A! \6 _; f7 F- d' D        参考宋宝华老师的《Linux设备驱动开发详解》第6章——字符设备驱动。
    1 I4 z* Y3 G- Q. p7 ]. R8 j- U$ b7 e/ Q/ N$ i: |9 x6 |( q

      k" I1 j/ ^/ R2 Q& o$ y$ U& S6 b
            根据我自己的理解总结一下编写字符驱动模块的一般步骤:, G; v& j6 o& N/ C$ O( {: e
    ) D  c# L! p0 z3 ?  p8 U+ B
            1.根据自己编程习惯选择包含cdev的自定义结构体或直接使用cdev结构体。# \% `8 F- u5 _  O/ g4 Y5 c
    6 i+ S* h* l' s" s; y
            2.例化file_operations结构体,然后填充文件操作的有关成员函数,并根据自己要求编写有关成员函数操作。, b3 W+ a# E: ?# r

    " d. {* P! z4 s  q- k+ P7 s9 ]        3.编写模块加载函数,包括io资源申请注册,中断号申请注册,设备号申请注册,内存申请,注册字符设备,5 v5 ?8 q+ L* U+ o6 d- `- B2 Z
    0 M+ Q7 h3 a# T9 E4 J# T) P
            4.编写模块卸装函数,加载模块的逆操作。
    1 k+ _( ]+ W7 ^+ L& {' G3 p
    . v' c+ H! v2 d* z8 t0 d' m( b2 Q        5.封装成2.6内核的驱动设备模型platform机制,包括编写platform_driver模块加载函数和卸装函数,填充platform_driver结构体的probe,remove,suspend,resume等成员,编写各成员函数。3 X. n* Q0 W% G, F; G) Y! l

    ; I3 D3 n9 Z( C* R        6.编写应用层的测试代码。
    ) ~: c: Q$ s# a( G- k* p- x" {) {8 b3 A1 |& P
            好,到资源栏下载我自己编写的simple gpio controller的字符驱动,对照一面的一般步骤一步步看代码( C* o2 V8 f, H+ A

    : J( o5 N2 v. y; H  I# f; ]
    0 N5 j0 b7 l. Q6 x8 \1 {
    9 X: W+ E4 z8 c- y        1.cdev结构体,这里我选择编写包含cdev的自定义结构体
    7 ^+ k5 o4 M( W) B# v, e/ H" V' `5 Y$ I% R7 P2 D
    • struct simple_gpio{
    •     void __iomem *base;
    •     struct cdev gpio_cdev;
    • };. N9 X8 f. @; P% U0 U) l

    0 O* j* {6 I2 r
    + ?4 h/ T9 C) X* X        2.file_operations结构体( G' Z4 N6 }. |/ c& r

    2 Q5 a  N1 @. T4 K7 Z        例化file_operations类,gpio controller operation! Y. q( ^1 Y6 J" b/ x  |
    + h5 ~2 |- ]& K+ i/ Z2 q5 u+ q
    • 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,
    • };
        e# k% K1 t, J8 v8 n2 A, X

    ' `/ @0 P3 b4 j( K8 D( D" B5 k% q6 Z2 e$ s5 }. X0 z9 }
            编写file_operation成员函数,只实现open和ioctl函数* P- ?6 k% Q4 h+ n# B& k& r

    1 c  p6 ]0 _9 M) o9 x) h
    • /******************************************* 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;
    • }
      / W3 V2 h9 D; \* c' `
         ' x( Q; A3 h( |; d3 f) w1 m5 a% m

    0 S) j# U9 h5 ]+ k) N        3.模块加载函数- i# t. \8 e& ?# U% j

    6 V. e" z: t, H        这里说明下,函数大部分流程我放在platform driver的probe函数中实现' z; l- W$ _3 n0 Q! t( [5 q

    0 h9 w$ Q9 T4 C- ]5 j
    • /******************************************* 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;
    • }
      6 k. V4 e' g* K, p4 c. {" V

    + [8 b" u) x9 ^3 G$ \
    , j. H  l  A" }' ?5 [2 k8 U/ [. D" b1 h2 T: R7 T! H
            4.模块卸装函数8 O' w: ]. L. Y# `- Y2 Q3 _
    # l! b& R2 m& \% `3 S
            与加载函数相同,流程放在platform driver的remove函数中实现4 h7 o% O: A& z1 v3 U
    6 n3 a+ t! D$ Q% u  ?
    • /******************************************* 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;
    • }. X! I/ T/ x' T/ d' v+ p  I2 m+ G8 Y

    ! X$ H$ T% J  g( ]7 g7 i+ B0 ^7 v# X" r& B) [$ L
            5.封装成platform机制* |: @& d$ u  Y0 }3 |# X6 M! l

    1 U; ]% f! z3 ]; E        对于这个步骤,基本上是一个固定的格式,个人理解就是通用的字符设备驱动套进去platform机制,至于这个机制,很多blog都有解释,这里就不详细再说明了,主要是platform_device,platform_driver,bus三者之间的关系,platform_driver有一系列的操作函数,platform_device对设备的属性描述。% p% A3 N1 w: I% ?2 E" ]
    - N9 _% l' G; _/ K
    • #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);
    • }. e# }, ^7 J( t5 t4 I! {
      V6 F- B) v7 t4 D  @" c# n

    * \# t# o/ w3 n9 m6 |3 M4 c8 J# R" ]
            6.测试文件# I2 `+ y0 X6 y, ?8 [1 E: Z) }; g

    * r6 r; \( i3 L) K8 q! R3 F        在linux应用层去做文件打开、读写、关闭操作相信学C的时候就应该有深刻的理解,这里的我们在驱动上没有实现read和write函数的具体操作,只实现了ioctl的操作,所以测试文件很简单,目的是看到LED灯闪烁的现象,所以只是简单打开设备文件,执行在驱动中定义好的命令而已。5 ~. M0 y* Z4 X' q

    2 f$ ]8 O% J/ I
    • //------------------------------------- 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;
    • }3 L  u: z8 y1 J9 Q

    $ A$ U5 c* }: X) r9 H/ r: s
    # F, d* G- `4 u1 {/ ^        至于代码中有少量的注释,或者大家可以自己理解理解,当是自学的过程,主要还是参考宋宝华老师的书,有问题的话留言大家交流交流。
  • TA的每日心情

    2019-11-29 15:37
  • 签到天数: 1 天

    [LV.1]初来乍到

    2#
    发表于 2021-7-26 14:50 | 只看该作者
    or1200下linux简单gpio字符设备驱动

    该用户从未签到

    3#
    发表于 2021-7-26 14:50 | 只看该作者
    or1200下linux简单gpio字符设备驱动

    该用户从未签到

    4#
    发表于 2021-7-26 14:51 | 只看该作者
    or1200下linux简单gpio字符设备驱动
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

    推荐内容上一条 /1 下一条

    EDA365公众号

    关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

    GMT+8, 2025-6-22 13:21 , Processed in 0.109375 second(s), 26 queries , Gzip On.

    深圳市墨知创新科技有限公司

    地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

    快速回复 返回顶部 返回列表