导航

    全志在线开发者论坛

    • 注册
    • 登录
    • 搜索
    • 版块
    • 话题
    • 在线文档
    • 社区主页

    【T113 S3】【spi驱动】【DMA 连续内存分配】【dma_alloc_coherent】【失败】

    其它全志芯片讨论区
    2
    9
    3468
    正在加载更多帖子
    • 从旧到新
    • 从新到旧
    • 最多赞同
    回复
    • 在新帖中回复
    登录后回复
    此主题已被删除。只有拥有主题管理权限的用户可以查看。
    • S
      shiguojie1989 LV 3 最后由 编辑

      项目需要用到spi和FPGA通讯,使用dma物理内存和虚拟内存地址映射,供应用程序直接调用内存。
      驱动代码通过了编译,并且在imx6ull上可以跑通,运行在T113上,加载驱动就会报错,报错内容提示:dma物理内存申请失败,没有找到原因。
      请大佬提供思路,并留下联系方式,如果能解决,愿意发红包。

      # insmod fsfpgain.ko
      [ 1525.955277] fsfpgain_probe
      [ 1525.958904] fsfpgain major=243, minor=0
      [ 1525.965357] *device:0xc4abe400,device:0xbf00237c
      [ 1525.971775] fsfpgaindev.nd name:fsfpgain@0
      [ 1525.972340] fsfpgaindev request cs&irq gpio ok
      [ 1525.983232] irq_gpio_spi_handler run
      [ 1525.987790] fsfpgaindev request_threaded_irq ok
      [ 1525.992980] spi_setup ret:0
      [ 1525.996247] fsfpgain probe ok
      [ 1525.999671] fsfpgain Debug:005
      [ 1526.003191] rxvadr:0x0 padr:0x0
      [ 1526.006846] rxvadr:0xbf002394 padr:0xbf002398
      [ 1526.011832] txvadr:0x0 padr:0x0
      [ 1526.015456] 8<--- cut here ---
      [ 1526.018909] Unable to handle kernel NULL pointer dereference at virtual address 00000008
      [ 1526.028004] pgd = 37cc6d18
      [ 1526.031038] [00000008] *pgd=44a83835, *pte=00000000, *ppte=00000000
      [ 1526.038098] Internal error: Oops: 17 [#1] PREEMPT SMP ARM
      [ 1526.044155] Modules linked in: fsfpgain(+)
      [ 1526.048758] CPU: 0 PID: 5484 Comm: insmod Not tainted 5.4.61 #16
      [ 1526.055495] Hardware name: Generic DT based system
      [ 1526.060875] PC is at memcpy+0x48/0x330
      [ 1526.065092] LR is at dma_alloc_coherent.constprop.5+0x34/0x68 [fsfpgain]
      [ 1526.072608] pc : [<c06375a8>]    lr : [<bf0009cc>]    psr: 20030013
      [ 1526.079635] sp : c4b83a14  ip : 00000000  fp : bf00138f
      [ 1526.085493] r10: c65b4c00  r9 : bf002308  r8 : 00000001
      [ 1526.091352] r7 : 00000cc1  r6 : bf002398  r5 : 00000040  r4 : 00000000
      [ 1526.098673] r3 : ae70a2b7  r2 : 000001f0  r1 : 00000008  r0 : c4b83a30
      [ 1526.105995] Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
      [ 1526.113999] Control: 10c5387d  Table: 44a4406a  DAC: 00000051
      [ 1526.120444] Process insmod (pid: 5484, stack limit = 0x95a75581)
      [ 1526.127181] Stack: (0xc4b83a14 to 0xc4b84000)
      [ 1526.132067] 3a00:                                              00000040 bf002398 00000cc1
      [ 1526.141247] 3a20: 00000001 c4b83a30 00000000 bf0009cc c709d900 c70b9e80 c0b03cc0 00000000
      [ 1526.150427] 3a40: 00000000 00000000 c0b02d00 c0142494 c76b7dc0 00000000 00000000 c088c885
      [ 1526.159608] 3a60: 00000001 c0a3fd80 00010001 c4b83b10 c4b83a70 c4b83a70 c70b9e80 00000000
      [ 1526.168787] 3a80: 00000000 00000000 c76b7d80 c709d914 00000000 00000001 00000000 00000001
      [ 1526.177967] 3aa0: c76b13ac 00000000 00000000 00000020 00000000 00000002 00000000 c4b83abc
      [ 1526.187148] 3ac0: c4b83abc ae70a2b7 00000000 c70b9e80 00000000 c4b83bcb 00000006 00000010
      [ 1526.196329] 3ae0: c4b83b0c c0647f68 00002e38 00000000 c4b83b10 ffffffff 00000000 00000000
      [ 1526.205510] 3b00: 00000001 00000000 c0b03bc8 31383332 00003031 ae70a2b7 c086923d c0888e4c
      [ 1526.214690] 3b20: c7061d40 ae70a2b7 c4b82000 c4b83bb4 c4b83bcb c4b83bb8 ffffffff c08476e4
      [ 1526.223870] 3b40: c4b83b64 c4b83bd1 c4b83bb8 c064a374 00000609 ffff0a10 c0b03bc8 00000001
      [ 1526.233052] 3b60: 0007b36e 00000600 ffff0a10 ae70a2b7 00000001 c0b03bc8 c0b6d248 c060ef5c
      [ 1526.242233] 3b80: c4b83b90 c0636820 c0b03bc8 c0636868 8dcd23b7 ae70a2b7 c0b80ae4 00000060
      [ 1526.251414] 3ba0: 000026d1 c03bdba0 c4b83bb8 c0636820 c0b03bc8 c0636868 8dcd2cda ae70a2b7
      [ 1526.260595] 3bc0: c0b80ae4 00000060 000026d1 c03bdba0 c0b80ae4 00000001 20030093 c0b0bc5c
      [ 1526.269776] 3be0: 20030093 c0652c74 c0b0bc5c 00000000 c0b67a40 c014e67c c0b67534 00000000
      [ 1526.278957] 3c00: 00000000 c06531a8 c0b67530 c0155e24 00000400 c0b0bc5c 00000187 00000000
      [ 1526.288137] 3c20: 60030013 60030013 00000187 00000000 c086922c c0851b4e 00000001 c0a39428
      [ 1526.297314] 3c40: c76b142c c018bf3c c0a3942c 00000014 60030013 c01572d8 00000001 c01574cc
      [ 1526.306495] 3c60: bf00153a c4b83cbc 000000eb 00000001 c643fa3c c0b03bc8 bf002300 00000000
      [ 1526.315673] 3c80: 00000000 00000001 bf002308 c65b4c00 bf00138f c0157518 bf00153a c4b83cbc
      [ 1526.324850] 3ca0: 00000040 bf002398 00000cc1 ae70a2b7 bf002300 bf002300 bf002300 00000000
      [ 1526.334027] 3cc0: 00000000 bf000880 bf00147d bf002308 c0895157 bf002100 00000007 bf002000
      [ 1526.343204] 3ce0: c65b4c00 00000000 bf002010 c0b8134c 00000007 00000000 c4b83f38 c041f0a4
      [ 1526.352381] 3d00: c65b4c00 00000000 c0b81350 c03d1e8c c65b4c00 bf002010 bf002010 c03d250c
      [ 1526.361558] 3d20: 00000000 c4bc7064 00000848 c03d2320 bf002010 c65b4c00 ae70a2b7 00000000
      [ 1526.370734] 3d40: c65b4c00 bf002010 c03d250c 00000000 c4bc7064 c03d24f4 00000000 c65b4c00
      [ 1526.379912] 3d60: bf002010 c03d25b8 c65b4c00 c0b03bc8 bf002010 c03d0400 c738d6bc c738d6a8
      [ 1526.389089] 3d80: c657fab4 ae70a2b7 c738d6bc bf002010 00000000 c49bcc80 c0b3e520 c03d131c
      [ 1526.398266] 3da0: bf0015e1 00000001 00000000 bf002010 c0b65000 c0b03bc8 bf005000 c03d2d98
      [ 1526.407444] 3dc0: ffffe000 c0b65000 c0b03bc8 bf005010 ffffe000 c0102f60 c086922c c0a428e0
      [ 1526.416621] 3de0: c7002200 60040013 00000cc0 c01897b0 c4b83e18 00000000 c4b83f38 c01d3e60
      [ 1526.425798] 3e00: 00000000 c7002200 00000cc0 c01897b0 c4b82000 c01d4254 c08b1b2c bf002100
      [ 1526.434975] 3e20: 00000001 ae70a2b7 bf002100 00000001 c4bc7180 c4bc7040 00000001 c01897ec
      [ 1526.444152] 3e40: bf002100 00000001 bf002100 00000001 bf002148 c0188430 bf00210c 00007fff
      [ 1526.453329] 3e60: bf002100 c0185b18 c4bc7064 c96a4300 c4bc7048 00000124 01c8d008 00000000
      [ 1526.462505] 3e80: 00000000 00000000 00000cc0 ae70a2b7 bf000000 c499f700 00000000 00000000
      [ 1526.471681] 3ea0: 00000000 00000000 00000000 00000000 6e72656b 00006c65 00000000 00000000
      [ 1526.480855] 3ec0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
      [ 1526.490030] 3ee0: 00000000 00000000 00000000 ae70a2b7 7fffffff 00000000 c0b03bc8 01c8d008
      [ 1526.499207] 3f00: 00000003 c0101228 c4b82000 0000017b 00000000 c0188af0 7fffffff 00000000
      [ 1526.508383] 3f20: 00000003 c01d56f8 00000001 c9677000 0002d328 00000000 c9677e44 c96782c0
      [ 1526.517560] 3f40: c9677000 0002d328 c96a3ab8 c9698682 c96991e0 00003000 000033c0 00001de8
      [ 1526.526737] 3f60: 000035a5 00000000 00000000 00000000 00001dd8 00000034 00000035 0000001e
      [ 1526.535914] 3f80: 00000000 00000019 00000000 ae70a2b7 0000002d 01c8d008 bec2df27 bec2de24
      [ 1526.545091] 3fa0: 0000017b c0101000 01c8d008 bec2df27 00000003 01c8d008 00000000 bec2df27
      [ 1526.554268] 3fc0: 01c8d008 bec2df27 bec2de24 0000017b bec2df27 00000000 b6fb8000 00000000
      [ 1526.563446] 3fe0: bec2dc88 bec2dc78 00026fb8 b6f14242 80040030 00000003 00000000 00000000
      [ 1526.572635] [<c06375a8>] (memcpy) from [<bf0009cc>] (dma_alloc_coherent.constprop.5+0x34/0x68 [fsfpgain])
      [ 1526.583386] [<bf0009cc>] (dma_alloc_coherent.constprop.5 [fsfpgain]) from [<bf000880>] (fsfpgain_probe+0x290/0x3a8 [fsfpgain])
      [ 1526.596181] [<bf000880>] (fsfpgain_probe [fsfpgain]) from [<c041f0a4>] (spi_drv_probe+0x78/0xa0)
      [ 1526.606051] [<c041f0a4>] (spi_drv_probe) from [<c03d1e8c>] (really_probe+0x168/0x394)
      [ 1526.614844] [<c03d1e8c>] (really_probe) from [<c03d2320>] (driver_probe_device+0x10c/0x154)
      [ 1526.624237] [<c03d2320>] (driver_probe_device) from [<c03d24f4>] (device_driver_attach+0x44/0x5c)
      [ 1526.634196] [<c03d24f4>] (device_driver_attach) from [<c03d25b8>] (__driver_attach+0xac/0xb8)
      [ 1526.643780] [<c03d25b8>] (__driver_attach) from [<c03d0400>] (bus_for_each_dev+0x64/0xa0)
      [ 1526.652981] [<c03d0400>] (bus_for_each_dev) from [<c03d131c>] (bus_add_driver+0xdc/0x1bc)
      [ 1526.662172] [<c03d131c>] (bus_add_driver) from [<c03d2d98>] (driver_register+0xb0/0xf8)
      [ 1526.671169] [<c03d2d98>] (driver_register) from [<bf005010>] (fsfpgain_init+0x10/0x1000 [fsfpgain])
      [ 1526.681332] [<bf005010>] (fsfpgain_init [fsfpgain]) from [<c0102f60>] (do_one_initcall+0x70/0x194)
      [ 1526.691390] [<c0102f60>] (do_one_initcall) from [<c01897ec>] (do_init_module+0x54/0x1dc)
      [ 1526.700476] [<c01897ec>] (do_init_module) from [<c0188430>] (load_module+0x18f4/0x1dd4)
      [ 1526.709459] [<c0188430>] (load_module) from [<c0188af0>] (sys_finit_module+0x94/0xb4)
      [ 1526.718251] [<c0188af0>] (sys_finit_module) from [<c0101000>] (ret_fast_syscall+0x0/0x54)
      [ 1526.727424] Exception stack(0xc4b83fa8 to 0xc4b83ff0)
      [ 1526.733092] 3fa0:                   01c8d008 bec2df27 00000003 01c8d008 00000000 bec2df27
      [ 1526.742269] 3fc0: 01c8d008 bec2df27 bec2de24 0000017b bec2df27 00000000 b6fb8000 00000000
      [ 1526.751443] 3fe0: bec2dc88 bec2dc78 00026fb8 b6f14242
      [ 1526.757112] Code: ba000002 f5d1f03c f5d1f05c f5d1f07c (e8b151f8)
      [ 1526.764119] ---[ end trace c5b41e868c50cbd3 ]---
      Segmentation fault
      
      

      驱动入库程序如下:

      #include "fsfpgain.h"
      
      static int fsfpgain_open(struct inode *inode, struct file *filp)
      {	
      	if (atomic_dec_and_test(&valid))//原子操作 唯一应用打开驱动
      	{
      		return 0;
      	}
      	atomic_inc(&valid);
      
      	return -EBUSY;
      }
      
      static int fsfpgain_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
      {
      }
      
      static long fsfpgain_ioctl(struct file *file, uint32_t cmd, unsigned long arg)
      {	
      }
      static int fsfpgain_mmap(struct file *filp, struct vm_area_struct *vma)
      {
            return ret;
      }
      
      static int fsfpgain_release(struct inode *inode, struct file *filp)
      {
      	valid.counter =1;//open函数访问,原子锁初始化
      	return 0;
      }
      
      static void init_p_buf_cut(void)
      {
      }
      
      static void spi_fpga_recv(struct buffer_cut *_p, int _no)
      {
      }
      
      static irqreturn_t irq_gpio_spi_handler(int irq, void *dev_id)
      {
      }
      
      static irqreturn_t irq_gpio_spi_thread_func(int irq, void *data)
      {
      }
      
      static const struct file_operations fsfpgain_fops = {
      	.owner = THIS_MODULE,
      	.open  = fsfpgain_open,
      	.read  = fsfpgain_read,
      	.release= fsfpgain_release,
      	.unlocked_ioctl = fsfpgain_ioctl,
      	.mmap  = fsfpgain_mmap,
      };
      
      static int fsfpgain_probe(struct spi_device *spi)
      {
      	int ret = 0;
      	valid.counter =1;//open函数访问,原子锁初始化
      	//valid = ATOMIC_INIT(1);
      	printk("fsfpgain_probe\r\n");
      
      	fsfpgaindev.major = 0;
      	if(fsfpgaindev.major)
      	{
      		fsfpgaindev.devid = MKDEV(fsfpgaindev.major, 0);
      		ret = register_chrdev_region(fsfpgaindev.devid, FSFPGAIN_CNT, FSFPGAIN_NAME);
      	}else{
      		ret = alloc_chrdev_region(&fsfpgaindev.devid, 0, FSFPGAIN_CNT, FSFPGAIN_NAME);
      		fsfpgaindev.major = MAJOR(fsfpgaindev.devid);
      		fsfpgaindev.minor = MINOR(fsfpgaindev.devid);
      	}
      	if(ret < 0)
      	{
      		printk("fsfpgain chrdev_region err!\r\n");
      		goto fail_devid;
      	}
      	printk("fsfpgain major=%d, minor=%d\r\n", fsfpgaindev.major, fsfpgaindev.minor);
      
      	fsfpgaindev.cdev.owner = THIS_MODULE;	
      	/* 2、注册设备 */
      	cdev_init(&fsfpgaindev.cdev, &fsfpgain_fops);
      	ret = cdev_add(&fsfpgaindev.cdev, fsfpgaindev.devid, FSFPGAIN_CNT);
      	if(0 > ret)
      	{
      		goto fail_cdev;
      	}
      	/* 3、创建类 */
      	fsfpgaindev.class = class_create(THIS_MODULE, FSFPGAIN_NAME);
      	if(IS_ERR(fsfpgaindev.class))
      	{
      		ret = PTR_ERR(fsfpgaindev.device);
      		goto fail_class;
      	}
      	/* 4、创建设备 */
      	fsfpgaindev.device = device_create(fsfpgaindev.class, NULL, fsfpgaindev.devid, NULL, FSFPGAIN_NAME);
      	if(IS_ERR(fsfpgaindev.device)){
      		ret = PTR_ERR(fsfpgaindev.device);
      		goto fail_device;
      	}
          printk("*device:0x%x,device:0x%x\r\n",fsfpgaindev.device,&fsfpgaindev.device);
      	//get nd
      	//fsfpgaindev.nd = of_get_parent(spi->dev.of_node);
          fsfpgaindev.nd = spi->dev.of_node;
      	if(NULL == fsfpgaindev.nd){
      		printk("fsfpgaindev.nd get error\r\n");
      		goto fail_gpio;
      	}else{
      		printk("fsfpgaindev.nd name:%s", fsfpgaindev.nd->full_name);
      	}
      
      	fsfpgaindev.cs_gpio = of_get_named_gpio(fsfpgaindev.nd, "cs-gpio", 0);
      	fsfpgaindev.irq_gpio   = of_get_named_gpio(fsfpgaindev.nd, "irq-gpio", 0);
      	if (fsfpgaindev.cs_gpio < 0)
      	{		
      		printk("fsfpgaindev.cs_gpio get error\r\n");
      		goto fail_gpio;
      		/* code */
      	}
      
      	if (fsfpgaindev.irq_gpio < 0)
      	{	
      		printk("fsfpgaindev.irq_gpio get error\r\n");
      		goto fail_gpio;
      		/* code */
      	}
      
      	//注册gpio
      	gpio_request(fsfpgaindev.cs_gpio, "cs_gpio");
      	gpio_request(fsfpgaindev.irq_gpio, "irq_gpio");
      	printk("fsfpgaindev request cs&irq gpio ok\r\n");
      
          //set gpio 
      	ret = gpio_direction_output(fsfpgaindev.cs_gpio, 1);
      	ret = gpio_direction_input(fsfpgaindev.irq_gpio);
      
      	//get irq num
      	fsfpgaindev.irq_num = gpio_to_irq(fsfpgaindev.irq_gpio);
      
      	//注册中断 线程化
      	//ret = request_irq(fsfpgaindev.irq_num, irq_gpio_spi_handler, IRQF_TRIGGER_FALLING, "irq_gpio", &fsfpgaindev);
      	ret = request_threaded_irq(fsfpgaindev.irq_num, irq_gpio_spi_handler,irq_gpio_spi_thread_func,IRQF_TRIGGER_RISING, "irq_gpio", &fsfpgaindev);
      	if(0 > ret)
      	{	
      		printk("request_irq error\r\n");
      		goto fail_irq;
      	}
      	printk("fsfpgaindev request_threaded_irq ok\r\n");
      
      	//init spi
      	spi->mode = SPI_MODE_1;
      	ret = spi_setup(spi);	
      	printk("spi_setup ret:%d\r\n",ret);
      	//set privete
      	fsfpgaindev.private_data = spi;
      
      	fsfpgaindev.b_workflag = 0;//初始化工作状态为不工作
      	fsfpgaindev.send_data_len = 0;//初始化spi收发数据长度为0
      	fsfpgaindev.recv_buf_no = 0;
      	fsfpgaindev.read_buf_no = 0;
      	fsfpgaindev.copy_recv_buf_no = 0;
      	printk("fsfpgain probe ok\r\n");
          
          memset(&fsfpgaindev,0,sizeof(fsfpgaindev));
          printk("fsfpgain Debug:005\r\n");
      	printk("rxvadr:0x%lx padr:0x%x\r\n",(long)fsfpgaindev.rxbuf.v_adr,fsfpgaindev.rxbuf.p_adr);
      	printk("rxvadr:0x%lx padr:0x%x\r\n",(long)&fsfpgaindev.rxbuf.v_adr,&fsfpgaindev.rxbuf.p_adr);
      	printk("txvadr:0x%lx padr:0x%x\r\n",(long)fsfpgaindev.txbuf.v_adr,fsfpgaindev.txbuf.p_adr);
      	fsfpgaindev.rxbuf.v_adr = dma_alloc_coherent(NULL,64,&fsfpgaindev.rxbuf.p_adr,GFP_KERNEL|GFP_DMA);
      	fsfpgaindev.txbuf.v_adr = dma_alloc_coherent(NULL,32,&fsfpgaindev.txbuf.p_adr,GFP_KERNEL|GFP_DMA);
      	
      	if((0x00000000 != fsfpgaindev.rxbuf.v_adr) & (0x00000000 != fsfpgaindev.txbuf.v_adr))
      	{
      		printk("dma_alloc_coherent ok\r\n");
      		printk("rxvadr:0x%lx padr:0x%x\r\n",(long)fsfpgaindev.rxbuf.v_adr,fsfpgaindev.rxbuf.p_adr);
      		printk("txvadr:0x%lx padr:0x%x\r\n",(long)fsfpgaindev.txbuf.v_adr,fsfpgaindev.txbuf.p_adr);
      	}
      	else{
      		printk("dma_alloc_coherent return NULL\r\n");
      		return -1;
      	}//申请大内存
      
      	return ret;
      
      fail_irq:
      	gpio_free(fsfpgaindev.cs_gpio);
      	gpio_free(fsfpgaindev.irq_gpio);
      fail_gpio:
      fail_device:
      	device_destroy(fsfpgaindev.class, fsfpgaindev.devid);
      fail_class:
      	class_destroy(fsfpgaindev.class);
      fail_cdev:
      	cdev_del(&fsfpgaindev.cdev);
      fail_devid:
      	unregister_chrdev_region(fsfpgaindev.devid,FSFPGAIN_CNT);
      	printk("fsfpgain probe error\r\n");
      
      	return ret;
      }
      
      static int fsfpgain_remove(struct spi_device *spi)
      {
      	int ret = 0;
      	//free irq
      	free_irq(fsfpgaindev.irq_num, &fsfpgaindev);
      	//gpio reset
      	gpio_direction_output(fsfpgaindev.cs_gpio, 0);
      	gpio_direction_output(fsfpgaindev.irq_gpio, 0);
      	//gpio free
      	gpio_free(fsfpgaindev.cs_gpio);
      	gpio_free(fsfpgaindev.irq_gpio);
      	cdev_del(&fsfpgaindev.cdev);
      	unregister_chrdev_region(fsfpgaindev.devid,FSFPGAIN_CNT);    
      	device_destroy(fsfpgaindev.class, fsfpgaindev.devid);	
      	class_destroy(fsfpgaindev.class);
      	printk("fsfpgain_remove\r\n");
      
      	return ret;
      }
      
      //传统匹配
      struct spi_device_id fsfpgain_id[] = {
      	{"fsfpgain", 0},
      	{}
      };
      
      //设备树匹配
      static const struct of_device_id fsfpgain_of_match[] = {
      	{ .compatible = "fsfpgain",},
      	{}
      };
      
      struct spi_driver fsfpgain = {
      	.probe		= fsfpgain_probe,
      	.remove     = fsfpgain_remove,
      	.id_table   = fsfpgain_id,
      	.driver = {
      		.name = "fsfpgain",
      		.owner = THIS_MODULE,
      		.of_match_table = fsfpgain_of_match,
      	},
      };
      
      //入口函数
      static int __init fsfpgain_init(void)
      {
      	int ret = 0;
      	//申请大内存
          //of_dma_configure();
          //struct device *dev = fsfpgain.dev;
      	
      	ret = spi_register_driver(&fsfpgain);
      	printk("fsfpgain_init spi_register_driver return:%d\r\n", ret);
      	
      	return ret;
      }
      
       //出口函数
      static void __exit fsfpgain_exit(void)
      {
      	//释放大内存
      	printk("rxvadr:0x%lx padr:0x%x\r\n",(long)fsfpgaindev.rxbuf.v_adr,fsfpgaindev.rxbuf.p_adr);
      	printk("txvadr:0x%lx padr:0x%x\r\n",(long)fsfpgaindev.txbuf.v_adr,fsfpgaindev.txbuf.p_adr);
      	dma_free_coherent(NULL,DMAC_MAX_TRF_SIZE*DMA_RECV_MAX_NUM,fsfpgaindev.rxbuf.v_adr, fsfpgaindev.rxbuf.p_adr);
      	dma_free_coherent(NULL,DMAC_MAX_TRF_SIZE*DMA_RECV_MAX_NUM,fsfpgaindev.txbuf.v_adr, fsfpgaindev.txbuf.p_adr);	
      	printk("rxvadr:0x%lx padr:0x%x\r\n",(long)fsfpgaindev.rxbuf.v_adr,fsfpgaindev.rxbuf.p_adr);
      	printk("txvadr:0x%lx padr:0x%x\r\n",(long)fsfpgaindev.txbuf.v_adr,fsfpgaindev.txbuf.p_adr);
      	spi_unregister_driver(&fsfpgain);
      	printk("fsfpgain_exit\r\n");
      }
      
      module_init(fsfpgain_init);
      module_exit(fsfpgain_exit);
      
      MODULE_LICENSE("GPL");
      MODULE_AUTHOR("fsml");
      MODULE_DESCRIPTION("fsfpgain t113 module");
      MODULE_VERSION("v2.0.0.0");
      

      头文件如下:

      #ifndef ___FSFPGAIN_H
      #define ___FSFPGAIN_H
      
      #include <linux/types.h>
      #include <linux/kernel.h>
      #include <linux/delay.h>
      #include <linux/ide.h>
      #include <linux/init.h>
      #include <linux/module.h>
      #include <linux/errno.h>
      #include <linux/gpio.h>
      #include <linux/cdev.h>
      #include <linux/device.h>
      #include <linux/of_gpio.h>
      #include <linux/semaphore.h>
      #include <linux/timer.h>
      #include <linux/spi/spi.h>
      #include <linux/of.h>
      #include <linux/of_address.h>
      #include <linux/of_gpio.h>
      #include <linux/platform_device.h>
      #include <linux/atomic.h>
      #include <linux/irq.h>
      //#include <linux/irqreturn.h>
      #include <linux/of_irq.h>
      #include <asm/mach/map.h>
      #include <asm/uaccess.h>
      #include <asm/io.h>
      
      //#define _DEBUG
      //#define _DEBUGWZG
      
      #define DMAC_CNLL_MAX_NUM           16
      #define DMAC_ONE_TR_NUM             3200 //3000
      #define DMAC_MAX_TRF_SIZE           DMAC_CNLL_MAX_NUM*DMAC_ONE_TR_NUM
      #define DMA_RECV_MAX_NUM            10//环形缓冲器大小
      
      #define NEXT_BUF(x) (((x+1) < DMA_RECV_MAX_NUM) ? (x+1) : 0)
      
      #define FSFPGAIN_CNT 1
      #define FSFPGAIN_NAME "fsfpgain@0"
      
      struct mem_addr{
      	char* v_adr;
      	dma_addr_t p_adr;
      };
      
      //环形缓冲器,分块保存每个dma头物理地址
      struct buffer_cut{
      	unsigned int recv_num;
      	unsigned int last_buf_len;//最后一块内存的长度
      	dma_addr_t pr_buf_fpga[DMA_RECV_MAX_NUM][DMAC_CNLL_MAX_NUM];
      	dma_addr_t pt_buf_fpga[DMA_RECV_MAX_NUM][DMAC_CNLL_MAX_NUM];
      };
      
      struct fsfpgain_dev  {
      	int major;
      	int minor;
      	dev_t devid;
      	struct cdev cdev;
      	struct class *class;
      	struct device *device;
      	struct spi_device *private_data;
      	int cs_gpio; //io1 20片选引脚
      	int irq_gpio;//io1 30中断引脚
      	int irq_num; //中断号
      	struct device_node *nd;//设备树节点
      	struct mem_addr rxbuf;
      	struct mem_addr txbuf;
      	struct spi_message m;
      	struct spi_transfer t;
      	int spi_len;//spi dma 单次长度
      	bool b_workflag;//工作状态
      	bool b_printk;//调试打印状态
      	unsigned int send_data_len;//与fpga通讯一次交换数据的长度
      	struct semaphore spi_sema;//信号量 表示环形缓冲区内的可读数据量
      	int recv_buf_no;//接收到的数据块号码
      	int copy_recv_buf_no;//被复制的“接收到的数据块号码”防止被中断修改数据
      	int read_buf_no;//可读取的数据块号码
      	//struct tasklet_struct tasklet;//中断下半部
      };
      
      //_p 环形缓冲器指针数组结构体 _no环形缓冲器编号
      //完成一次从fpga读取数据
      static void spi_fpga_recv(struct buffer_cut *_p, int _no);
      
      /*调用一次dma spi传输,启动dma要求长度是32的倍数
      **0 == _len % 32 
      */
      //static int spi_dma_recv(void *_ptx,void *_prx, unsigned int _len);
      //中断上半部,使用中断线程化,上半部可有可无,非必须
      static irqreturn_t irq_gpio_spi_handler(int irq, void *dev_id);
      
      /*初始化缓冲器指针数组结构体
      **驱动被板载ioctl设置fpga传输长度后,就可以把环形缓冲器每次传输的头地址设置好
      **后期使用不再计算
      */
      static void init_p_buf_cut(void);
      //中断下半部处理,调用一次spi收发函数
      //没有使用dma接收完成中断,使用imx自带的dma驱动,将其放入线程,不影响板载程序调用,待测试效果
      static irqreturn_t irq_gpio_spi_thread_func(int irq, void *data);
      
      static struct fsfpgain_dev fsfpgaindev;
      static struct buffer_cut buf_cut;
      static atomic_t valid;//原子操作 唯一应用打开驱动
      
      irq_handler_t (*handler)(int, void*);
      #endif
      
      
      1 条回复 最后回复 回复 引用 分享 0
      • A
        awwwwa LV 8 最后由 awwwwa 编辑

        参考 G2D 驱动做下修改

        lichee/linux-5.4/drivers/char/sunxi_g2d/g2d_rcq/g2d.c
        
        void *g2d_malloc(__u32 bytes_num, __u32 *phy_addr)
        {
        	void *address = NULL;
        
        #if defined(CONFIG_ION)
        	u32 actual_bytes;
        
        	if (bytes_num != 0) {
        		actual_bytes = G2D_BYTE_ALIGN(bytes_num);
        
        		address = dma_alloc_coherent(para.dev, actual_bytes,
        					     (dma_addr_t *) phy_addr,
        					     GFP_KERNEL);
        		if (address) {
        			return address;
        		}
        		G2D_ERR_MSG("dma_alloc_coherent fail, size=0x%x\n", bytes_num);
        		return NULL;
        	}
        	G2D_ERR_MSG("size is zero\n");
        #else
        	unsigned int map_size = 0;
        	struct page *page;
        
        	if (bytes_num != 0) {
        		map_size = PAGE_ALIGN(bytes_num);
        		page = alloc_pages(GFP_KERNEL, get_order(map_size));
        		if (page != NULL) {
        			address = page_address(page);
        			if (address == NULL) {
        				free_pages((unsigned long)(page),
        					   get_order(map_size));
        				G2D_ERR_MSG("page_address fail!\n");
        				return NULL;
        			}
        			*phy_addr = virt_to_phys(address);
        			return address;
        		}
        		G2D_ERR_MSG("alloc_pages fail!\n");
        		return NULL;
        	}
        	G2D_ERR_MSG("size is zero\n");
        #endif
        
        	return NULL;
        }
        

        目前看到 dma_alloc_coherent(NULL, xxx...) 的第一个参数是NULL,正常来说应该分配设备而不是NULL。

        S 2 条回复 最后回复 回复 引用 分享 1
        • S
          shiguojie1989 LV 3 @awwwwa 最后由 编辑

          @awwwwa 谢谢老师,我尝试一下,我之前也看到过这个地方的问题,也有说第一个参数可以为NULL,我也传入过spidev->dev,现象一样,我测试下您发的驱动参考。

          1 条回复 最后回复 回复 引用 分享 0
          • S
            shiguojie1989 LV 3 @awwwwa 最后由 编辑

            @awwwwa 在 【T113 S3】【spi驱动】【DMA 连续内存分配】【dma_alloc_coherent】【失败】 中说:

            参考 G2D 驱动做下修改

            lichee/linux-5.4/drivers/char/sunxi_g2d/g2d_rcq/g2d.c
            
            void *g2d_malloc(__u32 bytes_num, __u32 *phy_addr)
            {
            	void *address = NULL;
            
            #if defined(CONFIG_ION)
            	u32 actual_bytes;
            
            	if (bytes_num != 0) {
            		actual_bytes = G2D_BYTE_ALIGN(bytes_num);
            
            		address = dma_alloc_coherent(para.dev, actual_bytes,
            					     (dma_addr_t *) phy_addr,
            					     GFP_KERNEL);
            		if (address) {
            			return address;
            		}
            		G2D_ERR_MSG("dma_alloc_coherent fail, size=0x%x\n", bytes_num);
            		return NULL;
            	}
            	G2D_ERR_MSG("size is zero\n");
            #else
            	unsigned int map_size = 0;
            	struct page *page;
            
            	if (bytes_num != 0) {
            		map_size = PAGE_ALIGN(bytes_num);
            		page = alloc_pages(GFP_KERNEL, get_order(map_size));
            		if (page != NULL) {
            			address = page_address(page);
            			if (address == NULL) {
            				free_pages((unsigned long)(page),
            					   get_order(map_size));
            				G2D_ERR_MSG("page_address fail!\n");
            				return NULL;
            			}
            			*phy_addr = virt_to_phys(address);
            			return address;
            		}
            		G2D_ERR_MSG("alloc_pages fail!\n");
            		return NULL;
            	}
            	G2D_ERR_MSG("size is zero\n");
            #endif
            
            	return NULL;
            }
            

            目前看到 dma_alloc_coherent(NULL, xxx...) 的第一个参数是NULL,正常来说应该分配设备而不是NULL。

            我尝试了把fsfpgaindev.device作为参数传进去,还是不行。报错是一样的。

            
            	fsfpgaindev.rxbuf.v_adr = dma_alloc_coherent(fsfpgaindev.device,64,&fsfpgaindev.rxbuf.p_adr,GFP_KERNEL|GFP_DMA);
            	fsfpgaindev.txbuf.v_adr = dma_alloc_coherent(fsfpgaindev.device,32,&fsfpgaindev.txbuf.p_adr,GFP_KERNEL|GFP_DMA);
            
            A 1 条回复 最后回复 回复 引用 分享 0
            • A
              awwwwa LV 8 @shiguojie1989 最后由 编辑

              @shiguojie1989

              f9101d65-74da-4084-8d96-e782dd70356f-image.png

              这里的memset的作用是什么

              1 条回复 最后回复 回复 引用 分享 0
              • A
                awwwwa LV 8 最后由 awwwwa 编辑

                4ef27411-89ca-475d-95b0-cde38aae6f8a-image.png

                #include "fsfpgain.h"
                
                static int fsfpgain_open(struct inode *inode, struct file *filp) {
                  if (atomic_dec_and_test(&valid)) // 原子操作 唯一应用打开驱动
                  {
                    return 0;
                  }
                  atomic_inc(&valid);
                
                  return -EBUSY;
                }
                
                static int fsfpgain_read(struct file *filp, char __user *buf, size_t cnt,
                                         loff_t *off) {}
                
                static long fsfpgain_ioctl(struct file *file, uint32_t cmd, unsigned long arg) {
                }
                
                static int fsfpgain_mmap(struct file *filp, struct vm_area_struct *vma) {
                  return 0;
                }
                
                static int fsfpgain_release(struct inode *inode, struct file *filp) {
                  valid.counter = 1; // open函数访问,原子锁初始化
                  return 0;
                }
                
                static void init_p_buf_cut(void) {}
                
                static void spi_fpga_recv(struct buffer_cut *_p, int _no) {}
                
                static irqreturn_t irq_gpio_spi_handler(int irq, void *dev_id) {}
                
                static irqreturn_t irq_gpio_spi_thread_func(int irq, void *data) {}
                
                static const struct file_operations fsfpgain_fops = {
                    .owner = THIS_MODULE,
                    .open = fsfpgain_open,
                    .read = fsfpgain_read,
                    .release = fsfpgain_release,
                    .unlocked_ioctl = fsfpgain_ioctl,
                    .mmap = fsfpgain_mmap,
                };
                
                static int fsfpgain_probe(struct spi_device *spi) {
                  int ret = 0;
                  valid.counter = 1; // open函数访问,原子锁初始化
                  // valid = ATOMIC_INIT(1);
                  printk("fsfpgain_probe\r\n");
                
                  fsfpgaindev.major = 0;
                  if (fsfpgaindev.major) {
                    fsfpgaindev.devid = MKDEV(fsfpgaindev.major, 0);
                    ret =
                        register_chrdev_region(fsfpgaindev.devid, FSFPGAIN_CNT, FSFPGAIN_NAME);
                  } else {
                    ret =
                        alloc_chrdev_region(&fsfpgaindev.devid, 0, FSFPGAIN_CNT, FSFPGAIN_NAME);
                    fsfpgaindev.major = MAJOR(fsfpgaindev.devid);
                    fsfpgaindev.minor = MINOR(fsfpgaindev.devid);
                  }
                  if (ret < 0) {
                    printk("fsfpgain chrdev_region err!\r\n");
                    goto fail_devid;
                  }
                  printk("fsfpgain major=%d, minor=%d\r\n", fsfpgaindev.major,
                         fsfpgaindev.minor);
                
                  fsfpgaindev.cdev.owner = THIS_MODULE;
                  /* 2、注册设备 */
                  cdev_init(&fsfpgaindev.cdev, &fsfpgain_fops);
                  ret = cdev_add(&fsfpgaindev.cdev, fsfpgaindev.devid, FSFPGAIN_CNT);
                  if (0 > ret) {
                    goto fail_cdev;
                  }
                  /* 3、创建类 */
                  fsfpgaindev.class = class_create(THIS_MODULE, FSFPGAIN_NAME);
                  if (IS_ERR(fsfpgaindev.class)) {
                    ret = PTR_ERR(fsfpgaindev.device);
                    goto fail_class;
                  }
                  /* 4、创建设备 */
                  fsfpgaindev.device = device_create(fsfpgaindev.class, NULL, fsfpgaindev.devid,
                                                     NULL, FSFPGAIN_NAME);
                  if (IS_ERR(fsfpgaindev.device)) {
                    ret = PTR_ERR(fsfpgaindev.device);
                    goto fail_device;
                  }
                  printk("*device:0x%x,device:0x%x\r\n", fsfpgaindev.device,
                         &fsfpgaindev.device);
                  // get nd
                  // fsfpgaindev.nd = of_get_parent(spi->dev.of_node);
                  fsfpgaindev.nd = spi->dev.of_node;
                  if (NULL == fsfpgaindev.nd) {
                    printk("fsfpgaindev.nd get error\r\n");
                    goto fail_gpio;
                  } else {
                    printk("fsfpgaindev.nd name:%s", fsfpgaindev.nd->full_name);
                  }
                
                  fsfpgaindev.cs_gpio = of_get_named_gpio(fsfpgaindev.nd, "cs-gpio", 0);
                  fsfpgaindev.irq_gpio = of_get_named_gpio(fsfpgaindev.nd, "irq-gpio", 0);
                  if (fsfpgaindev.cs_gpio < 0) {
                    printk("fsfpgaindev.cs_gpio get error\r\n");
                    goto fail_gpio;
                    /* code */
                  }
                
                  if (fsfpgaindev.irq_gpio < 0) {
                    printk("fsfpgaindev.irq_gpio get error\r\n");
                    goto fail_gpio;
                    /* code */
                  }
                
                  // 注册gpio
                  gpio_request(fsfpgaindev.cs_gpio, "cs_gpio");
                  gpio_request(fsfpgaindev.irq_gpio, "irq_gpio");
                  printk("fsfpgaindev request cs&irq gpio ok\r\n");
                
                  // set gpio
                  ret = gpio_direction_output(fsfpgaindev.cs_gpio, 1);
                  ret = gpio_direction_input(fsfpgaindev.irq_gpio);
                
                  // // get irq num
                  // fsfpgaindev.irq_num = gpio_to_irq(fsfpgaindev.irq_gpio);
                
                  // // 注册中断 线程化
                  // // ret = request_irq(fsfpgaindev.irq_num, irq_gpio_spi_handler,
                  // // IRQF_TRIGGER_FALLING, "irq_gpio", &fsfpgaindev);
                  // ret = request_threaded_irq(fsfpgaindev.irq_num, irq_gpio_spi_handler,
                  //                            irq_gpio_spi_thread_func, IRQF_TRIGGER_RISING,
                  //                            "irq_gpio", &fsfpgaindev);
                  // if (0 > ret) {
                  //   printk("request_irq error\r\n");
                  //   goto fail_irq;
                  // }
                  // printk("fsfpgaindev request_threaded_irq ok\r\n");
                
                  // init spi
                  spi->mode = SPI_MODE_1;
                  ret = spi_setup(spi);
                  printk("spi_setup ret:%d\r\n", ret);
                  // set privete
                  fsfpgaindev.private_data = spi;
                
                  fsfpgaindev.b_workflag = 0;    // 初始化工作状态为不工作
                  fsfpgaindev.send_data_len = 0; // 初始化spi收发数据长度为0
                  fsfpgaindev.recv_buf_no = 0;
                  fsfpgaindev.read_buf_no = 0;
                  fsfpgaindev.copy_recv_buf_no = 0;
                  printk("fsfpgain probe ok\r\n");
                
                  printk("fsfpgain Debug:005\r\n");
                  printk("rxvadr:0x%lx padr:0x%x\r\n", (long)fsfpgaindev.rxbuf.v_adr, fsfpgaindev.rxbuf.p_adr);
                  printk("rxvadr:0x%lx padr:0x%x\r\n", (long)&fsfpgaindev.rxbuf.v_adr,  &fsfpgaindev.rxbuf.p_adr);
                  printk("txvadr:0x%lx padr:0x%x\r\n", (long)fsfpgaindev.txbuf.v_adr, fsfpgaindev.txbuf.p_adr);
                
                  // 设置DMA掩码
                  if ((dma_set_coherent_mask(&spi->dev, DMA_BIT_MASK(32))) != 0) {
                      dev_err(&spi->dev, "Failed to set 32-bit DMA mask\n");
                      return -ENODEV;
                  }
                
                  fsfpgaindev.rxbuf.v_adr = dma_alloc_coherent(&spi->dev, 64, &fsfpgaindev.rxbuf.p_adr, GFP_KERNEL);
                  fsfpgaindev.txbuf.v_adr = dma_alloc_coherent(&spi->dev, 32, &fsfpgaindev.txbuf.p_adr, GFP_KERNEL);
                
                  if ((0x00000000 != fsfpgaindev.rxbuf.v_adr) &
                      (0x00000000 != fsfpgaindev.txbuf.v_adr)) {
                    printk("dma_alloc_coherent ok\r\n");
                    printk("rxvadr:0x%lx padr:0x%x\r\n", (long)fsfpgaindev.rxbuf.v_adr,
                           fsfpgaindev.rxbuf.p_adr);
                    printk("txvadr:0x%lx padr:0x%x\r\n", (long)fsfpgaindev.txbuf.v_adr,
                           fsfpgaindev.txbuf.p_adr);
                  } else {
                    printk("dma_alloc_coherent return NULL\r\n");
                    return -1;
                  } // 申请大内存
                
                  return ret;
                
                fail_irq:
                  gpio_free(fsfpgaindev.cs_gpio);
                  gpio_free(fsfpgaindev.irq_gpio);
                fail_gpio:
                fail_device:
                  device_destroy(fsfpgaindev.class, fsfpgaindev.devid);
                fail_class:
                  class_destroy(fsfpgaindev.class);
                fail_cdev:
                  cdev_del(&fsfpgaindev.cdev);
                fail_devid:
                  unregister_chrdev_region(fsfpgaindev.devid, FSFPGAIN_CNT);
                  printk("fsfpgain probe error\r\n");
                
                  return ret;
                }
                
                static int fsfpgain_remove(struct spi_device *spi) {
                  int ret = 0;
                  // free irq
                  free_irq(fsfpgaindev.irq_num, &fsfpgaindev);
                  // gpio reset
                  gpio_direction_output(fsfpgaindev.cs_gpio, 0);
                  gpio_direction_output(fsfpgaindev.irq_gpio, 0);
                  // gpio free
                  gpio_free(fsfpgaindev.cs_gpio);
                  gpio_free(fsfpgaindev.irq_gpio);
                  cdev_del(&fsfpgaindev.cdev);
                  unregister_chrdev_region(fsfpgaindev.devid, FSFPGAIN_CNT);
                  device_destroy(fsfpgaindev.class, fsfpgaindev.devid);
                  class_destroy(fsfpgaindev.class);
                  printk("fsfpgain_remove\r\n");
                
                  return ret;
                }
                
                // 传统匹配
                struct spi_device_id fsfpgain_id[] = {{"fsfpgain", 0}, {}};
                
                // 设备树匹配
                static const struct of_device_id fsfpgain_of_match[] = {
                    {
                        .compatible = "fsfpgain",
                    },
                    {}};
                
                struct spi_driver fsfpgain = {
                    .probe = fsfpgain_probe,
                    .remove = fsfpgain_remove,
                    .id_table = fsfpgain_id,
                    .driver =
                        {
                            .name = "fsfpgain",
                            .owner = THIS_MODULE,
                            .of_match_table = fsfpgain_of_match,
                        },
                };
                
                // 入口函数
                static int __init fsfpgain_init(void) {
                  int ret = 0;
                  ret = spi_register_driver(&fsfpgain);
                  printk("fsfpgain_init spi_register_driver return:%d\r\n", ret);
                
                  return ret;
                }
                
                // 出口函数
                static void __exit fsfpgain_exit(void) {
                  // 释放大内存
                  printk("rxvadr:0x%lx padr:0x%x\r\n", (long)fsfpgaindev.rxbuf.v_adr,
                         fsfpgaindev.rxbuf.p_adr);
                  printk("txvadr:0x%lx padr:0x%x\r\n", (long)fsfpgaindev.txbuf.v_adr,
                         fsfpgaindev.txbuf.p_adr);
                  dma_free_coherent(NULL, DMAC_MAX_TRF_SIZE * DMA_RECV_MAX_NUM,
                                    fsfpgaindev.rxbuf.v_adr, fsfpgaindev.rxbuf.p_adr);
                  dma_free_coherent(NULL, DMAC_MAX_TRF_SIZE * DMA_RECV_MAX_NUM,
                                    fsfpgaindev.txbuf.v_adr, fsfpgaindev.txbuf.p_adr);
                  printk("rxvadr:0x%lx padr:0x%x\r\n", (long)fsfpgaindev.rxbuf.v_adr,
                         fsfpgaindev.rxbuf.p_adr);
                  printk("txvadr:0x%lx padr:0x%x\r\n", (long)fsfpgaindev.txbuf.v_adr,
                         fsfpgaindev.txbuf.p_adr);
                  spi_unregister_driver(&fsfpgain);
                  printk("fsfpgain_exit\r\n");
                }
                
                module_init(fsfpgain_init);
                module_exit(fsfpgain_exit);
                
                MODULE_LICENSE("GPL");
                MODULE_AUTHOR("fsml");
                MODULE_DESCRIPTION("fsfpgain t113 module");
                MODULE_VERSION("v2.0.0.0");
                

                问题:

                1. 没有设置设备,丢了一个NULL进去,这个操作在Linux 5.4 内核中是不允许的
                2. 没有设置 DMA MASK,4.14之前内核可以,之后的不行
                3. memset把之前设置的参数全部清空了,不知道有什么作用
                S 3 条回复 最后回复 回复 引用 分享 0
                • S
                  shiguojie1989 LV 3 @awwwwa 最后由 编辑

                  @awwwwa 老师,问题解决了!非常感谢,我的电话:13643323722,您可以加我微信或者留下电话,我添加您。

                  1 条回复 最后回复 回复 引用 分享 0
                  • S
                    shiguojie1989 LV 3 @awwwwa 最后由 编辑

                    @awwwwa memset是个错误,尝试解决问题时遗留下来的。

                    1 条回复 最后回复 回复 引用 分享 0
                    • S
                      shiguojie1989 LV 3 @awwwwa 最后由 编辑

                      @awwwwa 老师你好,这个驱动还有个问题:中断只能响应40us以上的高电平才会执行中断,但是我的驱动里面中断函数里面是上升沿触发。我看教程是通过设备树来设置,是因为我是驱动程序里面设置,没有成功吗?还是有其他因素呢?
                      中断注册部分代码是:

                      ret = request_threaded_irq(fsfpgaindev.irq_num, irq_gpio_spi_handler,irq_gpio_spi_thread_func,IRQF_TRIGGER_RISING, "irq_gpio", &fsfpgaindev);
                      	if(0 > ret)
                      	{	
                      		printk("request_irq error\r\n");
                      		goto fail_irq;
                      	}
                          else
                          {
                              printk("fsfpgain irq_num:%d\n",fsfpgaindev.irq_num);
                          }
                      
                      1 条回复 最后回复 回复 引用 分享 0
                      • 1 / 1
                      • First post
                        Last post

                      Copyright © 2024 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号

                      行为准则 | 用户协议 | 隐私权政策