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

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

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

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

    [LV.1]初来乍到

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

    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        至于代码中有少量的注释,或者大家可以自己理解理解,当是自学的过程,主要还是参考宋宝华老师的书,有问题的话留言大家交流交流。
  • 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-11-24 03:06 , Processed in 0.171875 second(s), 26 queries , Gzip On.

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

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

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