导航

    全志在线开发者论坛

    • 注册
    • 登录
    • 搜索
    • 版块
    • 话题
    • 在线文档
    • 社区主页
    1. 主页
    2. allwinner_account
    3. 最佳
    • 资料
    • 关注 0
    • 粉丝 1
    • 我的积分 1037
    • 主题 4
    • 帖子 25
    • 最佳 3
    • 群组 0

    allwinner_account 发布的最佳帖子

    • 【萌新入门】D1-H编译运行第一个闪灯驱动

      纯小白, 头铁直接上的荔枝派的板子, 确实是入门比较困难, 结合着b站的一些教程和社区的资料, 倒腾很久终于写出了能用的闪灯驱动
      这里记录和分享下, 欢迎交流指点[社区都是大佬萌新瑟瑟发抖🤣 ]

      驱动程序如下

      #include <linux/init.h>
      #include <linux/module.h>
      #include <linux/fs.h>     //file_operations register_chrdev_region
      #include <linux/kdev_t.h> //MKDEV
      #include <linux/cdev.h>   //cdev_init
      #include <linux/uaccess.h>//copy_from_user
      #include <linux/hrtimer.h>//硬件定时器
      #include <linux/gpio.h>   //gpio
      
      #define DEV_LEDFLASH_TYPE 'k'
      #define DEV_LEDFLASH_GETVALUE  _IOR(DEV_LEDFLASH_TYPE, 0x32, int)
      #define DEV_LEDFLASH_SETVALUE  _IOW(DEV_LEDFLASH_TYPE, 0x33, int)
      
      #define LED_PIN_NUMBER  65  //PC1=32*2+1
      #define LED_FLASH_INTERVAL  500  //1hz
      
      static int major= 222;//主设备号
      static int minor = 0; //子设备号
      static dev_t devno;   //设备号
      struct class *cls;    //设备类
      struct device *class_dev = NULL;  //设备节点
      static struct hrtimer flashTimer; //定时器
      static int interval = LED_FLASH_INTERVAL;  //定时器中断间隔(ms)
      
      /**
       * @description: 定时器回调函数,这里用来闪灯
       * @param {hrtimer} *timer
       * @return {*}
       */
      static enum hrtimer_restart flashTimer_handler(struct hrtimer *timer){
        static int num= 0;
        if(interval!=0){
          __gpio_set_value(LED_PIN_NUMBER, num++%2);
          // 设置下一次中断间隔
          hrtimer_forward_now(&flashTimer, ms_to_ktime(interval));
          return HRTIMER_RESTART;
        }else{
          // 关闭led
          __gpio_set_value(LED_PIN_NUMBER, 0);
          // 关闭定时器
          return HRTIMER_NORESTART;
        }
      }
      
      /**
       * @description: 打开设备文件, 启用LED口和定时器
       * @param {inode} *inode
       * @param {file} *filep
       * @return {*}
       */
      static int ledflash_open(struct inode *inode, struct file *filep){
        int result;
      
      	printk("[DRV] ledflash_open()\n");
        
        /* GPIO初始化 */
        printk("[DRV] _____gpio_init____\n");
        // 获取LED_GPIO访问权
        result= gpio_request(LED_PIN_NUMBER, NULL);
        if(result< 0){
          printk(KERN_ERR "[DRV] gpio_request() failed:%d\n", result);
          gpio_free(LED_PIN_NUMBER);
          return -1;
        }
        // TODO:使用pinctrl还可以配置上下拉,没找到例子还没有测试
        // 设置为输出模式
        result= gpio_direction_output(LED_PIN_NUMBER, 1);
        if(result< 0){
          printk(KERN_ERR "[DRV] gpio_direction_output() failed:%d\n", result);
      		gpio_free(LED_PIN_NUMBER);
          return -1;
        }
      
        /* 定时器初始化 */
        printk("[DRV] _____hrtimer_init____\n");
        // 配置定时器为基于开机时间时钟的相对模式 //模式定义见time.h 和hrtimer.h
        hrtimer_init(&flashTimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
        // 设置回调函数
        flashTimer.function = &flashTimer_handler;
        // 开启定时器
        hrtimer_start(&flashTimer, ms_to_ktime(LED_FLASH_INTERVAL), HRTIMER_MODE_REL);
      
      	return 0; 
      }
      /**
       * @description: 关闭设备文件, 释放LED口和定时器
       * @param {inode} *inode
       * @param {file} *filep
       * @return {*}
       */
      static int ledflash_release(struct inode *inode, struct file *filep){
        // 取消定时器
        hrtimer_cancel(&flashTimer);
        // 关闭led
        __gpio_set_value(LED_PIN_NUMBER, 0);
        // 释放gpio控制权
      	gpio_free(LED_PIN_NUMBER);
      
        printk("[DRV] ledflash_release()\n");
      	return 0; 
      }
      /**
       * @description: 设备操作函数接口
       * @param {file} *filep
       * @param {unsigned int} cmd
       * @param {unsigned long} arg
       * @return {*}
       */
      long ledflash_ioctl(struct file *filep, unsigned int cmd, unsigned long arg){
      	long ret;
      	void __user *argp= (void __user *)arg;
      	int __user *p= argp; //参数
        int set_val= LED_FLASH_INTERVAL;
      
        // 检测参数合法
      	if(_IOC_TYPE(cmd)!= DEV_LEDFLASH_TYPE){
      		pr_err("[DRV] cmd %u,bad magic 0x%x/0x%x.\n",cmd,_IOC_TYPE(cmd), DEV_LEDFLASH_TYPE);
      		return -ENOTTY;
      	}
      	if(_IOC_DIR(cmd)& _IOC_READ)
      		ret= !access_ok((void __user*)arg, _IOC_SIZE(cmd));
      	else if(_IOC_DIR(cmd)& _IOC_WRITE)
      		ret= !access_ok((void __user*)arg, _IOC_SIZE(cmd));
      	if(ret){
      		pr_err("[DRV] bad access %ld.\n",ret);
      		return -EFAULT;
      	}
      
        // 根据不同操作函数指令指令
      	switch(cmd){
      		case DEV_LEDFLASH_GETVALUE:
      			ret = put_user(interval, p);
      			printk("[DRV] DEV_LEDFLASH_GETVALUE %d\n",interval);
      			break;
      		case DEV_LEDFLASH_SETVALUE:
      			ret = get_user(set_val, p);
            // 设置为0将关闭定时器
            if(interval== 0 && set_val> 0)
              hrtimer_restart(&flashTimer);
            interval= set_val;
            printk("[DRV] DEV_LEDFLASH_SETVALUE %d\n",interval);
      			break;
      		default:
      			return -EINVAL;
      	}
      	return ret;
      }
      
      /**
       * @description: 文件操作接口绑定
       */
      static struct file_operations ledflash_ops = {
      	.open = ledflash_open,
        .release= ledflash_release,
        // .read= ledflash_read,
        // .write= ledflash_write, 
        .unlocked_ioctl= ledflash_ioctl,
      };
      /**
       * @description: 初始化程序
       */
      static int ledflash_init(void){
      	int result;
      	printk("[DRV] _____ledflashdrv_init____\n");
      
        /* 设备初始化 */
        // 注册cdev设备号, 绑定标准文件接口
        // 查看验证cat /proc/devices | grep ledflash
        result = register_chrdev(major, "ledflashCd", &ledflash_ops);
      	if(result <0){
      		printk(KERN_ERR "[DRV] register_chrdev fail\n");
      		goto out_err_0;
      	}
        // 拼接主次设备号
        devno = MKDEV(major, minor);
        // 创建设备类 
        // 查看验证 ls /sys/class/ | grep ledflash
        cls = class_create(THIS_MODULE, "ledflashCls");
      	if (IS_ERR(cls)) {
      		printk(KERN_ERR "[DRV] class_create() failed for cls\n");
      		result = PTR_ERR(cls);
      		goto out_err_1;
      	}
        // 创建设备节点,绑定设备类和设备号
        // 查看验证 ls /dev | grep ledflash
        class_dev = device_create(cls, NULL, devno, NULL, "ledflashDev");
      	if (IS_ERR(class_dev)) {
          printk(KERN_ERR "[DRV] device_create() failed for class_dev\n");
      		result = PTR_ERR(class_dev);
      		goto out_err_2;
      	}
        printk("[DRV] _____ledflashdrv_init_successed____\n");
        
      	return 0;
      out_err_2:
      	class_destroy(cls);
      out_err_1:
      	unregister_chrdev(major,"ledflash");
      out_err_0:  
      	return 	result;
      }
      /**
       * @description: 卸载驱动,依次释放资源
       */
      static void ledflash_exit(void){
        device_destroy(cls, devno);
        class_destroy(cls);
        unregister_chrdev(major, "ledflashCd");
      
        printk("[DRV] _____ledflash_exit_____\n");
      	return;
      }
      
      // 安装驱动时可以设定主设备号
      // 设定参数 insmod led_flash.ko major=333
      module_param(major,  int,  0644);
      // 绑定驱动安装和卸载程序
      module_init(ledflash_init);  //insmod
      module_exit(ledflash_exit);  //rmmod
      // 驱动信息
      // 测试modinfo找不到驱动, depmod找不到指令
      // 需要手动将ko模块放到/lib/modules/5.4.61目录下
      MODULE_LICENSE("GPL");
      MODULE_AUTHOR("G");
      

      makefile如下, 完整编译内核用时太久了, 手动设置三个参数后即可单独编译上传, 调试方便很多

      # 完整内核编译会定义KERNELRELEASE
      ifneq ($(KERNELRELEASE),)
      obj-m := led_flash.o
      
      else
      # 打印一下进到这边直接编译了
      $(warning ____alone_compile____)
      # 配置为riscv架构
      export ARCH=riscv
      # 配置编译器
      export CROSS_COMPILE=/home/ubt/tina-d1-h/prebuilt/gcc/linux-x86/riscv/toolchain-thead-glibc/riscv64-glibc-gcc-thead_20200702/bin/riscv64-unknown-linux-gnu-
      # 配置内核源码位置
      KDIR = /home/ubt/tina-d1-h/out/d1-h-nezha/compile_dir/target/linux-d1-h-nezha/linux-5.4.61
      # 指定驱动程序源码位置(当前目录)
      PWD = $(shell pwd)
      # 将led_flash.o 编译成led_flash.ko, 而led_flash.o 则由make的自动推导功能编译led_flash.c 文件生成
      obj-m += led_flash.o
      all:
      	make -C $(KDIR) M=$(PWD) modules 
      clean:
      	make -C $(KDIR) M=$(PWD) clean
      
      endif
      

      Kconfig

      config LED_FLASH
        tristate "led_flash driver, gpio out put pulse by timer"
        help
          led_flash help msg.
      

      以上文件放在驱动目录下单独一个文件夹内,注意驱动目录的makefile和Kconfig需要添加该文件夹索引信息
      然后make kernel_menuconfig进入驱动项即可配置
      我尝试了直接上传ko文件安装报错, 还是直接mp更新了板子, 之后就可以直接上传更新驱动模块了
      驱动编写参考教程
      编写一个内核驱动,点亮 LED

      测试的应用程序

      #include <stdio.h>  // printf
      #include <unistd.h> // open file
      #include <sys/types.h>
      #include <sys/stat.h>
      #include <fcntl.h>
      #include <sys/ioctl.h>
      
      #define DEV_LEDFLASH_TYPE 'k'
      #define DEV_LEDFLASH_GETVALUE  _IOR(DEV_LEDFLASH_TYPE, 0x32, int)
      #define DEV_LEDFLASH_SETVALUE  _IOW(DEV_LEDFLASH_TYPE, 0x33, int)
      
      int fd;
      int interval= 500;
      
      int main(void)
      {
        // printf("Hell! O’ world, why won’t my code compile?\n\n");
        printf("[USER] led_flash_begin\n");
      
        fd= open("/dev/ledflashDev", O_RDWR);
        if(fd< 0){
      		perror("[USER] open failed\n");
      		return -1;
      	}
      
        // 读取默认值
        if(ioctl(fd, DEV_LEDFLASH_GETVALUE, &interval)< 0){
          perror("[USER] DEV_LEDFLASH_GETVALUE failed\n");
      		return -1;
        }
        printf("[USER] default interval= %d\n", interval);
        sleep(2);
      
        // 关闭
        interval = 0;
      	if(ioctl(fd, DEV_LEDFLASH_SETVALUE, &interval)< 0){
          perror("[USER] DEV_LEDFLASH_SETVALUE failed\n");
      		return -1;
        }
        if(ioctl(fd, DEV_LEDFLASH_GETVALUE, &interval)< 0){
      		perror("[USER] DEV_LEDFLASH_GETVALUE failed\n");
      		return -1;
      	}
      	printf("[USER] set interval= %d\n", interval);
        sleep(2);
      
        // 设置为2hz
      	interval = 250;
      	if(ioctl(fd, DEV_LEDFLASH_SETVALUE, &interval)< 0){
          perror("[USER] DEV_LEDFLASH_SETVALUE failed\n");
      		return -1;
        }
        if(ioctl(fd, DEV_LEDFLASH_GETVALUE, &interval)< 0){
      		perror("[USER] DEV_LEDFLASH_GETVALUE failed\n");
      		return -1;
      	}
      	printf("[USER] set interval= %d\n", interval);
        sleep(2);
      
        // 设置为5hz
        interval = 100;
      	if(ioctl(fd, DEV_LEDFLASH_SETVALUE, &interval)< 0){
          perror("[USER] DEV_LEDFLASH_SETVALUE failed\n");
      		return -1;
        }
        if(ioctl(fd, DEV_LEDFLASH_GETVALUE, &interval)< 0){
      		perror("[USER] DEV_LEDFLASH_GETVALUE failed\n");
      		return -1;
      	}
      	printf("[USER] set interval= %d\n", interval);
        sleep(2);
      
        close(fd);
        printf("[USER] steper_end\n");
      	
      	return 0;
      }
      

      makefile

      #设置编译链路径及工具
      CTOOL := riscv64-unknown-linux-gnu-
      CCL := /home/ubt/tina-d1-h/prebuilt/gcc/linux-x86/riscv/toolchain-thead-glibc/riscv64-glibc-gcc-thead_20200702
      CC := ${CCL}/bin/${CTOOL}gcc
      
      # build led_flash executable when user executes “make”
      led_flash: led_flash.o
      	$(CC) $(LDFLAGS) led_flash.o -o led_flash
      
      led_flash.o: led_flash.c
      	$(CC) $(CFLAGS) -c led_flash.c
      
      # remove object files and executable when user executes “make clean”
      clean:
      	rm *.o led_flash
      

      全部编译好后使用adb上传,安装运行效果如下

      root@TinaLinux:/mnt/exUDISK/app# insmod ../mod/led_flash.ko
      [ 2654.707249] led_flash: loading out-of-tree module taints kernel.
      [ 2654.714946] [DRV] _____ledflashdrv_init____
      [ 2654.720524] [DRV] _____ledflashdrv_init_successed____
      root@TinaLinux:/mnt/exUDISK/app# ./led_flash
      [USER] led_flash_begin[ 2664.649231] [DRV] ledflash_open()
      
      [ 2664.654850] [DRV] _____gpio_init____
      [ 2664.659067] [DRV] _____hrtimer_init____
      [ 2664.663458] [DRV] DEV_LEDFLASH_GETVALUE 500
      [USER] default interval= 500
      [ 2666.668404] [DRV] DEV_LEDFLASH_SETVALUE 0
      [ 2666.672975] [DRV] DEV_LEDFLASH_GETVALUE 0
      [USER] set interval= 0
      [ 2668.677738] [DRV] DEV_LEDFLASH_SETVALUE 250
      [ 2668.682480] [DRV] DEV_LEDFLASH_GETVALUE 250
      [USER] set interval= 250
      [ 2670.687414] [DRV] DEV_LEDFLASH_SETVALUE 100
      [ 2670.692243] [DRV] DEV_LEDFLASH_GETVALUE 100
      [USER] set interval= 100
      [ 2672.697095] [DRV] ledflash_release()
      [USER] steper_end
      

      闪灯测试完毕,后面就可以开始正式的深入研究了🤠

      发布在 MR Series
      allwinner_account
      allwinner_account
    • 回复: SD卡启动,如何挂载剩余容量为overlayfs

      试了下不配置downloadfile也是可行的,初始化时可以根据大小自动格式化系统

      formating /dev/by-name/UDISK to ext4
      mke2fs 1.46.4 (18-Aug-2021)
      [    4.827559] random: mkfs.ext4: uninitialized urandom read (16 bytes read)
      [    4.835361] random: mkfs.ext4: uninitialized urandom read (16 bytes read)
      Creating filesystem with 3516935 4k blocks and 879552 inodes
      Filesystem UUID: 84f444b8-02d2-4035-b7b1-ec4f15a8f417
      Superblock backups stored on blocks:
              32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208
      
      Allocating group tables: done
      Writing inode tables: done
      Creating journal (16384 blocks): [    6.281300]
      [    6.281300] insmod_device_driver
      [    6.281300]
      [    6.288462] sunxi_usb_udc 4100000.udc-controller: 4100000.udc-controller supply udc not found, using dummy regulator
      [    6.321295] hdmi_hpd_sys_config_release
      done
      Writing superblocks and filesystem accounting information: done
      
      formating /dev/by-name/rootfs_data to ext4
      mke2fs 1.46.4 (18-Aug-2021)
      [   17.598480] random: mkfs.ext4: uninitialized urandom read (16 bytes read)
      [   17.606271] random: mkfs.ext4: uninitialized urandom read (16 bytes read)
      Creating filesystem with 262144 4k blocks and 65536 inodes
      Filesystem UUID: 56bbb4d6-0db1-47ec-a35c-f66492e4fdad
      Superblock backups stored on blocks:
              32768, 98304, 163840, 229376
      
      Allocating group tables: done
      Writing inode tables: done
      Creating journal (8192 blocks): done
      Writing superblocks and filesystem accounting information: done
      

      就是大小还不太对,我再研究下🤤
      总之还是感谢大佬帮助🤠

      发布在 MR Series
      allwinner_account
      allwinner_account
    • 回复: SD卡启动,如何挂载剩余容量为overlayfs

      @whycan 可以了!!!😳

      root@TinaLinux:/# df -kh
      Filesystem                Size      Used Available Use% Mounted on
      /dev/root                16.3M     16.3M         0 100% /rom
      devtmpfs                492.8M         0    492.8M   0% /dev
      tmpfs                   498.9M     32.0K    498.9M   0% /tmp
      /dev/by-name/rootfs_data
                              891.0K     49.0K    802.0K   6% /overlay
      overlayfs:/overlay      891.0K     49.0K    802.0K   6% /
      /dev/by-name/UDISK       13.1G     24.0K     13.1G   0% /mnt/UDISK
      root@TinaLinux:/# mount
      /dev/root on /rom type squashfs (ro,noatime)
      devtmpfs on /dev type devtmpfs (rw,relatime,size=504672k,nr_inodes=126168,mode=755)
      proc on /proc type proc (rw,nosuid,nodev,noexec,noatime)
      sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,noatime)
      tmpfs on /tmp type tmpfs (rw,nosuid,nodev,noatime)
      /dev/by-name/rootfs_data on /overlay type ext4 (rw,relatime)
      overlayfs:/overlay on / type overlay (rw,noatime,lowerdir=/,upperdir=/overlay/upper,workdir=/overlay/work)
      devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,mode=600,ptmxmode=000)
      debugfs on /sys/kernel/debug type debugfs (rw,noatime)
      none on /sys/kernel/config type configfs (rw,relatime)
      adb on /dev/usb-ffs/adb type functionfs (rw,relatime)
      /dev/by-name/UDISK on /mnt/UDISK type ext4 (rw,relatime)
      

      刚才终于搞明白了fstab和sys_oartition.fex的对应关系重新配置了一下就可以了,现在是这么设置的

      config 'mount'
      	option  target		'/overlay'
      	option	device		'/dev/by-name/rootfs_data'
      	option	options		'rw,async'
      	option	enabled		'1'
      
      config 'mount'
      	option  target		'/mnt/UDISK'
      	option	device		'/dev/by-name/UDISK'
      	option	options		'rw,async'
      	option	enabled		'1'
      
      [partition]
          name         = rootfs_data
          size         = 2097152
          downloadfile = "/home/ubt/tina-d1-h/fex/2Gb.fex"
          user_type    = 0x8000
      
      [partition]
          name = UDISK
          user_type = 0x8000
      

      就是rootfs_data我设置的是2G,不知道为啥它变成891k了
      这个2Gb.fex我是用这个rootfs_data.fex生成的

      发布在 MR Series
      allwinner_account
      allwinner_account
    • 1 / 1