diff --git a/bsp/SCALP-FAST-ROUTER-FIRMWARE-BSP-V0.3.bsp b/bsp/SCALP-FAST-ROUTER-FIRMWARE-BSP-V0.3.bsp new file mode 100644 index 0000000000000000000000000000000000000000..9089c790715ec8a1afbda3c68723efc56b5cf9c6 Binary files /dev/null and b/bsp/SCALP-FAST-ROUTER-FIRMWARE-BSP-V0.3.bsp differ diff --git a/firmware-bin/BOOT.BIN b/firmware-bin/BOOT.BIN index c87bdef3f6311112dceb07daa48a8617366ca7c6..980bb35b82c7af54cc555eb36c8a63869c90b82f 100644 Binary files a/firmware-bin/BOOT.BIN and b/firmware-bin/BOOT.BIN differ diff --git a/firmware-bin/virtualfs-ext4.img b/firmware-bin/virtualfs-ext4.img index 934528fa653ff7db6d89d14a382f54668772c468..c2c686c7a5c1f4ae549bcd21cd67568ec70cb50a 100644 Binary files a/firmware-bin/virtualfs-ext4.img and b/firmware-bin/virtualfs-ext4.img differ diff --git a/firmware-bin/zynq_fsbl.elf b/firmware-bin/zynq_fsbl.elf index 3b5f9e4ba3c5b2f7d2b2bf20468800a53959fb7e..d30d4afc5efca11795b39aa348e1e24393a5c103 100644 Binary files a/firmware-bin/zynq_fsbl.elf and b/firmware-bin/zynq_fsbl.elf differ diff --git a/scalp_fast_router_firmware_petalinux/.petalinux/metadata b/scalp_fast_router_firmware_petalinux/.petalinux/metadata index e13cb8125220ef161fcc7066dfa162902086137a..cf353cad5859f18e717c6845054deb343566a550 100644 --- a/scalp_fast_router_firmware_petalinux/.petalinux/metadata +++ b/scalp_fast_router_firmware_petalinux/.petalinux/metadata @@ -1,6 +1,6 @@ PETALINUX_VER=2020.2 VALIDATE_HW_CHKSUM=1 -HARDWARE_CHECKSUM=7e6e3678e1be9e766e25618ba6237679 +HARDWARE_CHECKSUM=c1efaea1a10d8a6a949bed6944192b91 YOCTO_SDK=5ff8fc5f85d1566b314bb73eaa378212 -RFSCONFIG_CHKSUM=875fef99c2cf2a1f1c40d3a7bfaa5b32 +RFSCONFIG_CHKSUM=aaca83fde9c8a44dc5232f9f855bb35c HARDWARE_PATH=/home/jo/Documents/Projets/Hepia/scalp_project/scalp_firmware/designs/vivado/scalp_fast_router_firmware/2020.2/lin64/scalp_fast_router_firmware/scalp_fast_router_firmware.xsa diff --git a/scalp_fast_router_firmware_petalinux/SCALP-FAST-ROUTER-FIRMWARE-BSP-V0.3.bsp b/scalp_fast_router_firmware_petalinux/SCALP-FAST-ROUTER-FIRMWARE-BSP-V0.3.bsp new file mode 100644 index 0000000000000000000000000000000000000000..9089c790715ec8a1afbda3c68723efc56b5cf9c6 Binary files /dev/null and b/scalp_fast_router_firmware_petalinux/SCALP-FAST-ROUTER-FIRMWARE-BSP-V0.3.bsp differ diff --git a/scalp_fast_router_firmware_petalinux/project-spec/configs/config b/scalp_fast_router_firmware_petalinux/project-spec/configs/config index 9312953f848e6e486957652b62f50f5b97593d2e..aaea5216d4c97c5ffe10e2659db3a87992c33cc8 100644 --- a/scalp_fast_router_firmware_petalinux/project-spec/configs/config +++ b/scalp_fast_router_firmware_petalinux/project-spec/configs/config @@ -161,7 +161,7 @@ CONFIG_SUBSYSTEM_EXTRA_DT_FILES="" # CONFIG_SUBSYSTEM_BOOTARGS_AUTO=y CONFIG_SUBSYSTEM_BOOTARGS_EARLYPRINTK=y -CONFIG_SUBSYSTEM_BOOTARGS_GENERATED="console=ttyPS0,115200 earlycon" + CONFIG_SUBSYSTEM_DEVICETREE_COMPILER_FLAGS="-@" # CONFIG_SUBSYSTEM_DTB_OVERLAY is not set # CONFIG_SUBSYSTEM_REMOVE_PL_DTB is not set @@ -253,3 +253,4 @@ CONFIG_YOCTO_NETWORK_SSTATE_FEEDS_URL="http://petalinux.xilinx.com/sswreleases/r # User Layers # CONFIG_USER_LAYER_0="" +CONFIG_SUBSYSTEM_BOOTARGS_GENERATED="console=ttyPS0,115200 earlycon" diff --git a/scalp_fast_router_firmware_petalinux/project-spec/configs/rootfs_config b/scalp_fast_router_firmware_petalinux/project-spec/configs/rootfs_config index 5efa805dcbd78a5501e8f3b67d70715569ebeca1..78cfbbf20b569393cfbe461a450ce60326847b33 100644 --- a/scalp_fast_router_firmware_petalinux/project-spec/configs/rootfs_config +++ b/scalp_fast_router_firmware_petalinux/project-spec/configs/rootfs_config @@ -4037,6 +4037,11 @@ CONFIG_peekpoke=y CONFIG_sja1105-conf=y CONFIG_sja1105-init=y +# +# modules +# +CONFIG_u-dma-buf=y + # # user packages # diff --git a/scalp_fast_router_firmware_petalinux/project-spec/hw-description/scalp_fast_router_firmware.bit b/scalp_fast_router_firmware_petalinux/project-spec/hw-description/scalp_fast_router_firmware.bit index 27643205258b2ffd6e808126294edc1d19b4e998..797038e5dba1f6d97154c388173edcf38a64583b 100644 Binary files a/scalp_fast_router_firmware_petalinux/project-spec/hw-description/scalp_fast_router_firmware.bit and b/scalp_fast_router_firmware_petalinux/project-spec/hw-description/scalp_fast_router_firmware.bit differ diff --git a/scalp_fast_router_firmware_petalinux/project-spec/hw-description/system.xsa b/scalp_fast_router_firmware_petalinux/project-spec/hw-description/system.xsa index 9d18a600111936fc9fb3b85ba7e4f46d03fba38f..10da3f6c9e124c44b44670ea67bc4df57116ceaa 100644 Binary files a/scalp_fast_router_firmware_petalinux/project-spec/hw-description/system.xsa and b/scalp_fast_router_firmware_petalinux/project-spec/hw-description/system.xsa differ diff --git a/scalp_fast_router_firmware_petalinux/project-spec/meta-user/conf/user-rootfsconfig b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/conf/user-rootfsconfig index c1ae806a7855a73c5c4589a48ec3073282bf46ff..c930624194f85534552c7ca91484e671af6852ca 100644 --- a/scalp_fast_router_firmware_petalinux/project-spec/meta-user/conf/user-rootfsconfig +++ b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/conf/user-rootfsconfig @@ -16,3 +16,4 @@ CONFIG_scalp-locaddr CONFIG_scalplocaddr CONFIG_dmatest CONFIG_dmatest-devmem +CONFIG_u-dma-buf diff --git a/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-apps/dmatest/files/dmatest.cc b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-apps/dmatest/files/dmatest.cc index 2d59bc7882f5b440684a90ac209c46eaff759f0f..866e3b3b8bc28d72c708304ff6a9ce8c1831a7d9 100644 --- a/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-apps/dmatest/files/dmatest.cc +++ b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-apps/dmatest/files/dmatest.cc @@ -61,6 +61,13 @@ int main(int argc, char *argv[]) uint32_t dstx = 0, dsty = 0, dstz = 0; uint32_t srcx = 0, srcy = 0, srcz = 0; uint32_t type = 0, pld_size = 0; + uint32_t qos_phy_status_north; + uint32_t qos_phy_status_east; + uint32_t qos_phy_status_south; + uint32_t qos_phy_status_west; + uint32_t qos_phy_status_top; + uint32_t qos_phy_status_bottom; + uint32_t qos_phy_status_local; uint32_t send = 0; uint32_t receive = 0; uint32_t buffer_len = 0; @@ -220,6 +227,67 @@ int main(int argc, char *argv[]) scalp_socket->dma_set_src_buf_len(buffer_len); scalp_socket->dma_set_dst_buf_len(buffer_len); + // Get QoS Phy Status + // North + if((status = scalp_socket->get_qos_phy_status(&qos_phy_status_north, libscalp::E_QOS_PHY_STATUS_NORTH)) != libscalp::E_SCALP_DMA_NO_ERR) + { + scalp_socket->perror("main - scalp_socket->get_qos_phy_status()", status); + return EXIT_FAILURE; + } + + // Get QoS Phy Status + // East + if((status = scalp_socket->get_qos_phy_status(&qos_phy_status_east, libscalp::E_QOS_PHY_STATUS_EAST)) != libscalp::E_SCALP_DMA_NO_ERR) + { + scalp_socket->perror("main - scalp_socket->get_qos_phy_status()", status); + return EXIT_FAILURE; + } + + // Get QoS Phy Status + // South + if((status = scalp_socket->get_qos_phy_status(&qos_phy_status_south, libscalp::E_QOS_PHY_STATUS_SOUTH)) != libscalp::E_SCALP_DMA_NO_ERR) + { + scalp_socket->perror("main - scalp_socket->get_qos_phy_status()", status); + return EXIT_FAILURE; + } + + // Get QoS Phy Status + // West + if((status = scalp_socket->get_qos_phy_status(&qos_phy_status_west, libscalp::E_QOS_PHY_STATUS_WEST)) != libscalp::E_SCALP_DMA_NO_ERR) + { + scalp_socket->perror("main - scalp_socket->get_qos_phy_status()", status); + return EXIT_FAILURE; + } + + // Get QoS Phy Status + // Top + if((status = scalp_socket->get_qos_phy_status(&qos_phy_status_top, libscalp::E_QOS_PHY_STATUS_TOP)) != libscalp::E_SCALP_DMA_NO_ERR) + { + scalp_socket->perror("main - scalp_socket->get_qos_phy_status()", status); + return EXIT_FAILURE; + } + + // Get QoS Phy Status + // Bottom + if((status = scalp_socket->get_qos_phy_status(&qos_phy_status_bottom, libscalp::E_QOS_PHY_STATUS_BOTTOM)) != libscalp::E_SCALP_DMA_NO_ERR) + { + scalp_socket->perror("main - scalp_socket->get_qos_phy_status()", status); + return EXIT_FAILURE; + } + + // Get QoS Phy Status + // Local + if((status = scalp_socket->get_qos_phy_status(&qos_phy_status_local, libscalp::E_QOS_PHY_STATUS_LOCAL)) != libscalp::E_SCALP_DMA_NO_ERR) + { + scalp_socket->perror("main - scalp_socket->get_qos_phy_status()", status); + return EXIT_FAILURE; + } + + fprintf(stdout, "#> QoS GT Phy Status :\n\tNorth : %d\n\tEast : %d\n\tSouth : %d\n\tWest : %d\n\tTop : %d\n\tBottom : %d\n\tLocal : %d\n\n", + qos_phy_status_north, qos_phy_status_east, qos_phy_status_south, + qos_phy_status_west, qos_phy_status_top, qos_phy_status_bottom, + qos_phy_status_local); + if(def_locaddr == 1) { // Set local addr diff --git a/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-apps/libscalp/files/scalp.cc b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-apps/libscalp/files/scalp.cc index 321a0967b7ef22c840886cb2dcdfbc3f37da0f66..1bd47d8f6aac9fb65ab5235b3cce9c85b76d3713 100644 --- a/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-apps/libscalp/files/scalp.cc +++ b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-apps/libscalp/files/scalp.cc @@ -798,6 +798,7 @@ int32_t libscalp::scalp_socket::rcvd(void) #ifdef SCALP_LIB_DEBUG_MODE std::cout << "libscalp::scalp_socket::rcvd() : wait interrupt" << endl; #endif + // Sync on S2MM if((status = this->dma_s2mm_sync()) < 0) return status; @@ -825,6 +826,47 @@ int32_t libscalp::scalp_socket::rcvd(void) return E_SCALP_DMA_NO_ERR; } +int32_t libscalp::scalp_socket::rcvd_nb(void) +{ + int32_t status = 0; + + // Run MM2S channel + if((status = this->run_mm2s_chan()) != E_SCALP_DMA_NO_ERR) + return status; + + // Run S2MM channel + if((status = this->run_s2mm_chan()) != E_SCALP_DMA_NO_ERR) + return status; + + // Set trnsfr len + if((status = this->set_trnsfr_len(0)) != E_SCALP_DMA_NO_ERR) + return status; + + // Set buf len + if((status = this->set_buf_len(this->ddr_mem_dst_buf_len)) != E_SCALP_DMA_NO_ERR) + return status; + +#ifdef SCALP_LIB_DEBUG_MODE + std::cout << "libscalp::scalp_socket::rcvd_nb() : check interrupt" << endl; +#endif + + if((status & E_IOC_IRQ_FLAG) && (status & E_IDLE_FLAG)) + { +#ifdef SCALP_LIB_DEBUG_MODE + this->dma_s2mm_status(); + this->dma_mm2s_status(); +#endif + return E_SCALP_DMA_READ_TRSFR_CPLT; + } + +#ifdef SCALP_LIB_DEBUG_MODE + this->dma_s2mm_status(); + this->dma_mm2s_status(); +#endif + + return E_SCALP_DMA_NO_ERR; +} + int32_t libscalp::scalp_socket::set_locaddr(uint32_t x, uint32_t y, uint32_t z) { libscalp::scalp_fast_router_registers *_regs = NULL; @@ -861,6 +903,26 @@ int32_t libscalp::scalp_socket::get_locaddr(uint32_t *x, uint32_t *y, uint32_t * return E_SCALP_DMA_NO_ERR; } +int32_t libscalp::scalp_socket::get_qos_phy_status(uint32_t *qos_phy_status, uint32_t qos_phy_status_flag) +{ + libscalp::scalp_fast_router_registers *_regs = NULL; + uint32_t status = 0; + + if((_regs = reinterpret_cast<libscalp::scalp_fast_router_registers *>(this->uio_mmap_scalp_router_virtual_reg->get_ptr())) == NULL) + return E_SCALP_DMA_ERR_PTR; + + if(qos_phy_status != NULL) + { + status = (uint32_t)_regs->qos_phy_status; + *qos_phy_status = (uint32_t)((status >> qos_phy_status_flag) & 0x00000001); + } + else + return E_SCALP_DMA_ERR_SRC_ADDR; + + return E_SCALP_DMA_NO_ERR; +} + + uint32_t * libscalp::scalp_socket::set_header(uint32_t dstx, uint32_t dsty, uint32_t dstz, uint32_t pld_size, uint32_t type) { // libscalp::scalp_fast_router_registers *_regs = NULL; diff --git a/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-apps/libscalp/files/scalp.hh b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-apps/libscalp/files/scalp.hh index 0cf0e1c69796001dc0e63b2fdabc5e197233b1f0..7eb8b913349f1e3ad5ad984350bfcb54e0751a9c 100644 --- a/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-apps/libscalp/files/scalp.hh +++ b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-apps/libscalp/files/scalp.hh @@ -45,6 +45,19 @@ namespace libscalp std::atomic<uint32_t> dma_fifo_rx_wr_data_cnt; std::atomic<uint32_t> dma_fifo_rx_rd_data_cnt; std::atomic<uint32_t> dma_fifo_rx_status; + // QoS GT Phy Status + std::atomic<uint32_t> qos_phy_status; + }; + + enum scalp_qos_phy_status_flags + { + E_QOS_PHY_STATUS_NORTH = 0x0, + E_QOS_PHY_STATUS_EAST = 0x1, + E_QOS_PHY_STATUS_SOUTH = 0x2, + E_QOS_PHY_STATUS_WEST = 0x3, + E_QOS_PHY_STATUS_TOP = 0x4, + E_QOS_PHY_STATUS_BOTTOM = 0x5, + E_QOS_PHY_STATUS_LOCAL = 0x6 }; struct dma_registers @@ -88,6 +101,13 @@ namespace libscalp E_ENABLE_ALL_IRQ = 0x00007000 }; + enum scalp_dma_read_mode + { + E_SCALP_DMA_READ_BLOCKING_MODE = 0x0, + E_SCALP_DMA_READ_NON_BLOCKING_MODE = 0x1, + E_SCALP_DMA_READ_TRSFR_CPLT = 0x2 + }; + enum scalp_dma_ptr_type { E_SCALP_DMA_VIRTUAL_SRC_BUF = 0x0, @@ -219,8 +239,10 @@ namespace libscalp int32_t dma_init (void); int32_t send (void); int32_t rcvd (void); + int32_t rcvd_nb (void); int32_t set_locaddr (uint32_t x, uint32_t y, uint32_t z); int32_t get_locaddr (uint32_t *x, uint32_t *y, uint32_t *z); + int32_t get_qos_phy_status (uint32_t *qos_phy_status, uint32_t qos_phy_status_flag); uint32_t *set_header (uint32_t dstx, uint32_t dsty, uint32_t dstz, uint32_t pld_size, uint32_t type); uint32_t *get_header (uint32_t *dstx, uint32_t *dsty, uint32_t *dstz, uint32_t *srcx, uint32_t *srcy, uint32_t *srcz, diff --git a/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-bsp/device-tree/files/scalp-router.dtsi b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-bsp/device-tree/files/scalp-router.dtsi index 27f2fe444ef06e9d3d61a1bb68b0ec3c4fec942d..b964e53570592c8f26589790b188f87abb509550 100644 --- a/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-bsp/device-tree/files/scalp-router.dtsi +++ b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-bsp/device-tree/files/scalp-router.dtsi @@ -4,27 +4,36 @@ #size-cells = <1>; compatible = "simple-bus"; ranges ; - + scalp_fast_router_dma_virtual_addr_1: scalp_fast_router_dma_virtual_addr@0x40400000 { compatible = "generic-uio"; reg = <0x40400000 0x10000>; status = "okay"; }; + // udmabuf0@0 { + // compatible = "ikwzm,u-dma-buf"; + // device-name = "udmabuf0"; + // minor-number = <0>; + // size = <0x04000000>; // 64MiB + // //dma-coherent; + // memory-region = <&reserved_dmabuf_0>; + // }; + scalp_fast_router_virtual_src_addr_1: scalp_fast_router_virtual_src_addr@0x0e000000 { - compatible = "generic-uio"; - reg = <0x0e000000 0x10000>; - interrupt-parent = <&intc>; - interrupts = < 0 29 4 >; - status = "okay"; + compatible = "generic-uio"; + reg = <0x0e000000 0x10000>; + interrupt-parent = <&intc>; + interrupts = < 0 29 4 >; + status = "okay"; }; scalp_fast_router_virtual_dst_addr_1: scalp_fast_router_virtual_dst_addr@0x0f000000 { - compatible = "generic-uio"; - reg = <0x0f000000 0x10000>; - interrupt-parent = <&intc>; - interrupts = < 0 30 4 >; - status = "okay"; + compatible = "generic-uio"; + reg = <0x0f000000 0x10000>; + interrupt-parent = <&intc>; + interrupts = < 0 30 4 >; + status = "okay"; }; scalp_fast_router_registers_0: scalp_fast_router_registers@0x43c00000 { @@ -32,6 +41,7 @@ reg = <0x43c00000 0x1000>; status = "okay"; }; + }; }; diff --git a/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi index e9f6b87507129cffd70c017e838235516a860199..7a2de3eb7e6531ffa8c552c159f24f0a9a47776f 100644 --- a/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi +++ b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi @@ -35,6 +35,20 @@ reg = <0 0x10000000>; }; + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + reserved_dmabuf_0: buffer@0 { + compatible = "shared-dma-pool"; + reusable; + reg = <0x0 0x0c000000 0x0 0x04000000>; + linux,cma-default; + label = "reserved_dmabuf_0"; + }; + }; + chosen { bootargs = "earlyprintk cpuidle.off=1 crashkernel=256M rw"; stdout-path = "serial0:115200n8"; diff --git a/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-modules/u-dma-buf/README b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-modules/u-dma-buf/README new file mode 100644 index 0000000000000000000000000000000000000000..d1772ab6a6da5b53d66b19180774ca157028f2ce --- /dev/null +++ b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-modules/u-dma-buf/README @@ -0,0 +1,53 @@ +PetaLinux User Module Template +=================================== + +This directory contains a PetaLinux kernel module created from a template. + +If you are developing your module from scratch, simply start editing the +file u-dma-buf.c. + +You can easily import any existing module code by copying it into this +directory, and editing the automatically generated Makefile as described below. + +The "all:" target in the Makefile template will compile compile the module. + +Before building the module, you will need to enable the module from +PetaLinux menuconfig by running: + "petalinux-config -c rootfs" +You will see your module in the "modules --->" submenu. + +To compile and install your module to the target file system copy on the host, +simply run the command. + "petalinux-build -c kernel" to build kernel first, and then run + "petalinux-build -c u-dma-buf" to build the module + +You will also need to rebuild PetaLinux bootable images so that the images +is updated with the updated target filesystem copy, run this command: + "petalinux-build -c rootfs" + +You can also run one PetaLinux command to compile the module, install it +to the target filesystem host copy and update the bootable images as follows: + "petalinux-build" + +If OF(OpenFirmware) is configured, you need to add the device node to the +DTS(Device Tree Source) file so that the device can be probed when the module is +loaded. Here is an example of the device node in the device tree: + + u-dma-buf_instance: u-dma-buf@XXXXXXXX { + compatible = "vendor,u-dma-buf"; + reg = <PHYSICAL_START_ADDRESS ADDRESS_RANGE>; + interrupt-parent = <&INTR_CONTROLLER_INSTANCE>; + interrupts = < INTR_NUM INTR_SENSITIVITY >; + }; +Notes: + * "u-dma-buf@XXXXXXXX" is the label of the device node, it is usually the "DEVICE_TYPE@PHYSICAL_START_ADDRESS". E.g. "u-dma-buf@89000000". + * "compatible" needs to match one of the the compatibles in the module's compatible list. + * "reg" needs to be pair(s) of the physical start address of the device and the address range. + * If the device has interrupt, the "interrupt-parent" needs to be the interrupt controller which the interrupt connects to. and the "interrupts" need to be pair(s) of the interrupt ID and the interrupt sensitivity. + +For more information about the the DTS file, please refer to this document in the Linux kernel: linux-2.6.x/Documentation/powerpc/booting-without-of.txt + + +To add extra source code files (for example, to split a large module into +multiple source files), add the relevant .o files to the list in the local +Makefile where indicated. diff --git a/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-modules/u-dma-buf/files/COPYING b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-modules/u-dma-buf/files/COPYING new file mode 100644 index 0000000000000000000000000000000000000000..6d45519c8c6cc926a7d6d96ebc45de7226f66099 --- /dev/null +++ b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-modules/u-dma-buf/files/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-modules/u-dma-buf/files/Makefile b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-modules/u-dma-buf/files/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..d6b4c6851dfa90ba61acec38f332910fd68ca635 --- /dev/null +++ b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-modules/u-dma-buf/files/Makefile @@ -0,0 +1,17 @@ +obj-m := u-dma-buf.o + +MY_CFLAGS += -g -DDEBUG +ccflags-y += ${MY_CFLAGS} + +SRC := $(shell pwd) + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(SRC) + +modules_install: + $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install + +clean: + rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c + rm -f Module.markers Module.symvers modules.order + rm -rf .tmp_versions Modules.symvers diff --git a/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-modules/u-dma-buf/files/u-dma-buf.c b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-modules/u-dma-buf/files/u-dma-buf.c new file mode 100644 index 0000000000000000000000000000000000000000..3d19a1c1e9c55263cd327d08406dd850d7853e71 --- /dev/null +++ b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-modules/u-dma-buf/files/u-dma-buf.c @@ -0,0 +1,2134 @@ +/********************************************************************************* + * + * Copyright (C) 2015-2020 Ichiro Kawazome + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ********************************************************************************/ +#include <linux/cdev.h> +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/fs.h> +#include <linux/idr.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/sched.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/sysctl.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include <linux/scatterlist.h> +#include <linux/pagemap.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/version.h> +#include <asm/page.h> +#include <asm/byteorder.h> + +/** + * DOC: Udmabuf Constants + */ + +MODULE_DESCRIPTION("User space mappable DMA buffer device driver"); +MODULE_AUTHOR("ikwzm"); +MODULE_LICENSE("Dual BSD/GPL"); + +#define DRIVER_VERSION "3.2.4" +#define DRIVER_NAME "u-dma-buf" +#define DEVICE_NAME_FORMAT "udmabuf%d" +#define DEVICE_MAX_NUM 256 +#define UDMABUF_DEBUG 1 +#define USE_VMA_FAULT 1 +#define UDMABUF_MGR_ENABLE 1 +#define UDMABUF_MGR_NAME "u-dma-buf-mgr" + +#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)) && (defined(CONFIG_ARM) || defined(CONFIG_ARM64))) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) +#include <linux/dma-map-ops.h> +#else +#include <linux/dma-noncoherent.h> +#endif +#define IS_DMA_COHERENT(dev) dev_is_dma_coherent(dev) +#else +#define IS_DMA_COHERENT(dev) is_device_dma_coherent(dev) +#endif +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) +#define USE_DEV_GROUPS 1 +#else +#define USE_DEV_GROUPS 0 +#endif + +#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) && defined(CONFIG_OF)) +#define USE_OF_RESERVED_MEM 1 +#else +#define USE_OF_RESERVED_MEM 0 +#endif + +#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) && defined(CONFIG_OF)) +#define USE_OF_DMA_CONFIG 1 +#else +#define USE_OF_DMA_CONFIG 0 +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) +#define USE_DEV_PROPERTY 1 +#else +#define USE_DEV_PROPERTY 0 +#endif + +#if (UDMABUF_DEBUG == 1) +#define UDMABUF_DEBUG_CHECK(this,debug) (this->debug) +#else +#define UDMABUF_DEBUG_CHECK(this,debug) (0) +#endif + +#if (USE_OF_RESERVED_MEM == 1) +#include <linux/of_reserved_mem.h> +#endif + +#ifndef U64_MAX +#define U64_MAX ((u64)~0ULL) +#endif + +/** + * DOC: Udmabuf Static Variables + * + * * udmabuf_sys_class - udmabuf system class + * * init_enable - udmabuf install/uninstall infomation enable + * * dma_mask_bit - udmabuf dma mask bit + */ + +/** + * udmabuf_sys_class - udmabuf system class + */ +static struct class* udmabuf_sys_class = NULL; + +/** + * info_enable module parameter + */ +static int info_enable = 1; +module_param( info_enable , int, S_IRUGO); +MODULE_PARM_DESC( info_enable , "udmabuf install/uninstall infomation enable"); +#define DMA_INFO_ENABLE (info_enable & 0x02) + +/** + * dma_mask_bit module parameter + */ +static int dma_mask_bit = 32; +module_param( dma_mask_bit, int, S_IRUGO); +MODULE_PARM_DESC( dma_mask_bit, "udmabuf dma mask bit(default=32)"); + +/** + * DOC: Udmabuf Device Data Structure + * + * This section defines the structure of udmabuf device. + * + */ + +/** + * struct udmabuf_device_data - udmabuf device data structure. + */ +struct udmabuf_device_data { + struct device* sys_dev; + struct device* dma_dev; + struct cdev cdev; + dev_t device_number; + struct mutex sem; + bool is_open; + size_t size; + size_t alloc_size; + void* virt_addr; + dma_addr_t phys_addr; + int sync_mode; + u64 sync_offset; + size_t sync_size; + int sync_direction; + bool sync_owner; + u64 sync_for_cpu; + u64 sync_for_device; +#if (USE_OF_RESERVED_MEM == 1) + bool of_reserved_mem; +#endif +#if ((UDMABUF_DEBUG == 1) && (USE_VMA_FAULT == 1)) + bool debug_vma; +#endif +}; + +/** + * sync_mode(synchronous mode) value + */ +#define SYNC_MODE_INVALID (0x00) +#define SYNC_MODE_NONCACHED (0x01) +#define SYNC_MODE_WRITECOMBINE (0x02) +#define SYNC_MODE_DMACOHERENT (0x03) +#define SYNC_MODE_MASK (0x03) +#define SYNC_MODE_MIN (0x01) +#define SYNC_MODE_MAX (0x03) +#define SYNC_ALWAYS (0x04) + +/** + * DOC: Udmabuf System Class Device File Description + * + * This section define the device file created in system class when udmabuf is + * loaded into the kernel. + * + * The device file created in system class is as follows. + * + * * /sys/class/udmabuf/<device-name>/driver_version + * * /sys/class/udmabuf/<device-name>/phys_addr + * * /sys/class/udmabuf/<device-name>/size + * * /sys/class/udmabuf/<device-name>/sync_mode + * * /sys/class/udmabuf/<device-name>/sync_offset + * * /sys/class/udmabuf/<device-name>/sync_size + * * /sys/class/udmabuf/<device-name>/sync_direction + * * /sys/class/udmabuf/<device-name>/sync_owner + * * /sys/class/udmabuf/<device-name>/sync_for_cpu + * * /sys/class/udmabuf/<device-name>/sync_for_device + * * /sys/class/udmabuf/<device-name>/dma_coherent + * * + */ + +#define SYNC_COMMAND_DIR_MASK (0x000000000000000C) +#define SYNC_COMMAND_DIR_SHIFT (2) +#define SYNC_COMMAND_SIZE_MASK (0x00000000FFFFFFF0) +#define SYNC_COMMAND_SIZE_SHIFT (0) +#define SYNC_COMMAND_OFFSET_MASK (0xFFFFFFFF00000000) +#define SYNC_COMMAND_OFFSET_SHIFT (32) +#define SYNC_COMMAND_ARGMENT_MASK (0xFFFFFFFFFFFFFFFE) +/** + * udmabuf_sync_command_argments() - get argment for dma_sync_single_for_cpu() or dma_sync_single_for_device() + * + * @this: Pointer to the udmabuf device data structure. + * @command sync command (this->sync_for_cpu or this->sync_for_device) + * @phys_addr Pointer to the phys_addr for dma_sync_single_for_...() + * @size Pointer to the size for dma_sync_single_for_...() + * @direction Pointer to the direction for dma_sync_single_for_...() + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_sync_command_argments( + struct udmabuf_device_data *this , + u64 command , + dma_addr_t *phys_addr, + size_t *size , + enum dma_data_direction *direction +) { + u64 sync_offset ; + size_t sync_size ; + int sync_direction; + if ((command & SYNC_COMMAND_ARGMENT_MASK) != 0) { + sync_offset = (u64 )((command & SYNC_COMMAND_OFFSET_MASK) >> SYNC_COMMAND_OFFSET_SHIFT); + sync_size = (size_t)((command & SYNC_COMMAND_SIZE_MASK ) >> SYNC_COMMAND_SIZE_SHIFT ); + sync_direction = (int )((command & SYNC_COMMAND_DIR_MASK ) >> SYNC_COMMAND_DIR_SHIFT ); + } else { + sync_offset = this->sync_offset; + sync_size = this->sync_size; + sync_direction = this->sync_direction; + } + if (sync_offset + sync_size > this->size) + return -EINVAL; + switch(sync_direction) { + case 1 : *direction = DMA_TO_DEVICE ; break; + case 2 : *direction = DMA_FROM_DEVICE ; break; + default: *direction = DMA_BIDIRECTIONAL; break; + } + *phys_addr = this->phys_addr + sync_offset; + *size = sync_size; + return 0; +} + +/** + * udmabuf_sync_for_cpu() - call dma_sync_single_for_cpu() when (sync_for_cpu != 0) + * @this: Pointer to the udmabuf device data structure. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_sync_for_cpu(struct udmabuf_device_data* this) +{ + int status = 0; + + if (this->sync_for_cpu) { + dma_addr_t phys_addr; + size_t size; + enum dma_data_direction direction; + status = udmabuf_sync_command_argments(this, this->sync_for_cpu, &phys_addr, &size, &direction); + if (status == 0) { + dma_sync_single_for_cpu(this->dma_dev, phys_addr, size, direction); + this->sync_for_cpu = 0; + this->sync_owner = 0; + } + } + return status; +} + +/** + * udmabuf_sync_for_device() - call dma_sync_single_for_device() when (sync_for_device != 0) + * @this: Pointer to the udmabuf device data structure. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_sync_for_device(struct udmabuf_device_data* this) +{ + int status = 0; + + if (this->sync_for_device) { + dma_addr_t phys_addr; + size_t size; + enum dma_data_direction direction; + status = udmabuf_sync_command_argments(this, this->sync_for_device, &phys_addr, &size, &direction); + if (status == 0) { + dma_sync_single_for_device(this->dma_dev, phys_addr, size, direction); + this->sync_for_device = 0; + this->sync_owner = 1; + } + } + return status; +} + +#define DEF_ATTR_SHOW(__attr_name, __format, __value) \ +static ssize_t udmabuf_show_ ## __attr_name(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + ssize_t status; \ + struct udmabuf_device_data* this = dev_get_drvdata(dev); \ + if (mutex_lock_interruptible(&this->sem) != 0) \ + return -ERESTARTSYS; \ + status = sprintf(buf, __format, (__value)); \ + mutex_unlock(&this->sem); \ + return status; \ +} + +static inline int NO_ACTION(struct udmabuf_device_data* this){return 0;} + +#define DEF_ATTR_SET(__attr_name, __min, __max, __pre_action, __post_action) \ +static ssize_t udmabuf_set_ ## __attr_name(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) \ +{ \ + ssize_t status; \ + u64 value; \ + struct udmabuf_device_data* this = dev_get_drvdata(dev); \ + if (0 != mutex_lock_interruptible(&this->sem)){return -ERESTARTSYS;} \ + if (0 != (status = kstrtoull(buf, 0, &value))){ goto failed;} \ + if ((value < __min) || (__max < value)) {status = -EINVAL; goto failed;} \ + if (0 != (status = __pre_action(this))) { goto failed;} \ + this->__attr_name = value; \ + if (0 != (status = __post_action(this))) { goto failed;} \ + status = size; \ + failed: \ + mutex_unlock(&this->sem); \ + return status; \ +} + +DEF_ATTR_SHOW(driver_version , "%s\n" , DRIVER_VERSION ); +DEF_ATTR_SHOW(size , "%zu\n" , this->size ); +DEF_ATTR_SHOW(phys_addr , "%pad\n" , &this->phys_addr ); +DEF_ATTR_SHOW(sync_mode , "%d\n" , this->sync_mode ); +DEF_ATTR_SET( sync_mode , 0, 7, NO_ACTION, NO_ACTION ); +DEF_ATTR_SHOW(sync_offset , "0x%llx\n", this->sync_offset ); +DEF_ATTR_SET( sync_offset , 0, U64_MAX, NO_ACTION, NO_ACTION ); +DEF_ATTR_SHOW(sync_size , "%zu\n" , this->sync_size ); +DEF_ATTR_SET( sync_size , 0, SIZE_MAX, NO_ACTION, NO_ACTION ); +DEF_ATTR_SHOW(sync_direction , "%d\n" , this->sync_direction ); +DEF_ATTR_SET( sync_direction , 0, 2, NO_ACTION, NO_ACTION ); +DEF_ATTR_SHOW(sync_owner , "%d\n" , this->sync_owner ); +DEF_ATTR_SHOW(sync_for_cpu , "%llu\n" , this->sync_for_cpu ); +DEF_ATTR_SET( sync_for_cpu , 0, U64_MAX, NO_ACTION, udmabuf_sync_for_cpu ); +DEF_ATTR_SHOW(sync_for_device, "%llu\n" , this->sync_for_device ); +DEF_ATTR_SET( sync_for_device , 0, U64_MAX, NO_ACTION, udmabuf_sync_for_device); +#if defined(IS_DMA_COHERENT) +DEF_ATTR_SHOW(dma_coherent , "%d\n" , IS_DMA_COHERENT(this->dma_dev) ); +#endif +#if ((UDMABUF_DEBUG == 1) && (USE_VMA_FAULT == 1)) +DEF_ATTR_SHOW(debug_vma , "%d\n" , this->debug_vma ); +DEF_ATTR_SET( debug_vma , 0, 1, NO_ACTION, NO_ACTION ); +#endif + +static struct device_attribute udmabuf_device_attrs[] = { + __ATTR(driver_version , 0444, udmabuf_show_driver_version , NULL ), + __ATTR(size , 0444, udmabuf_show_size , NULL ), + __ATTR(phys_addr , 0444, udmabuf_show_phys_addr , NULL ), + __ATTR(sync_mode , 0664, udmabuf_show_sync_mode , udmabuf_set_sync_mode ), + __ATTR(sync_offset , 0664, udmabuf_show_sync_offset , udmabuf_set_sync_offset ), + __ATTR(sync_size , 0664, udmabuf_show_sync_size , udmabuf_set_sync_size ), + __ATTR(sync_direction , 0664, udmabuf_show_sync_direction , udmabuf_set_sync_direction ), + __ATTR(sync_owner , 0444, udmabuf_show_sync_owner , NULL ), + __ATTR(sync_for_cpu , 0664, udmabuf_show_sync_for_cpu , udmabuf_set_sync_for_cpu ), + __ATTR(sync_for_device, 0664, udmabuf_show_sync_for_device , udmabuf_set_sync_for_device), +#if defined(IS_DMA_COHERENT) + __ATTR(dma_coherent , 0444, udmabuf_show_dma_coherent , NULL ), +#endif +#if ((UDMABUF_DEBUG == 1) && (USE_VMA_FAULT == 1)) + __ATTR(debug_vma , 0664, udmabuf_show_debug_vma , udmabuf_set_debug_vma ), +#endif + __ATTR_NULL, +}; + +#if (USE_DEV_GROUPS == 1) + +#define udmabuf_device_attrs_size (sizeof(udmabuf_device_attrs)/sizeof(udmabuf_device_attrs[0])) + +static struct attribute* udmabuf_attrs[udmabuf_device_attrs_size] = { + NULL +}; +static struct attribute_group udmabuf_attr_group = { + .attrs = udmabuf_attrs +}; +static const struct attribute_group* udmabuf_attr_groups[] = { + &udmabuf_attr_group, + NULL +}; + +static inline void udmabuf_sys_class_set_attributes(void) +{ + int i; + for (i = 0 ; i < udmabuf_device_attrs_size-1 ; i++) { + udmabuf_attrs[i] = &(udmabuf_device_attrs[i].attr); + } + udmabuf_attrs[i] = NULL; + udmabuf_sys_class->dev_groups = udmabuf_attr_groups; +} +#else + +static inline void udmabuf_sys_class_set_attributes(void) +{ + udmabuf_sys_class->dev_attrs = udmabuf_device_attrs; +} + +#endif + +#if (USE_VMA_FAULT == 1) +/** + * DOC: Udmabuf Device VM Area Operations + * + * This section defines the operation of vm when mmap-ed the udmabuf device file. + * + * * udmabuf_device_vma_open() - udmabuf device vm area open operation. + * * udmabuf_device_vma_close() - udmabuf device vm area close operation. + * * udmabuf_device_vma_fault() - udmabuf device vm area fault operation. + * * udmabuf_device_vm_ops - udmabuf device vm operation table. + */ + +/** + * udmabuf_device_vma_open() - udmabuf device vm area open operation. + * @vma: Pointer to the vm area structure. + * Return: None + */ +static void udmabuf_device_vma_open(struct vm_area_struct* vma) +{ + struct udmabuf_device_data* this = vma->vm_private_data; + if (UDMABUF_DEBUG_CHECK(this, debug_vma)) + dev_info(this->dma_dev, "vma_open(virt_addr=0x%lx, offset=0x%lx)\n", vma->vm_start, vma->vm_pgoff<<PAGE_SHIFT); +} + +/** + * udmabuf_device_vma_close() - udmabuf device vm area close operation. + * @vma: Pointer to the vm area structure. + * Return: None + */ +static void udmabuf_device_vma_close(struct vm_area_struct* vma) +{ + struct udmabuf_device_data* this = vma->vm_private_data; + if (UDMABUF_DEBUG_CHECK(this, debug_vma)) + dev_info(this->dma_dev, "vma_close()\n"); +} + +/** + * VM_FAULT_RETURN_TYPE - Type of udmabuf_device_vma_fault() return value. + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) +typedef vm_fault_t VM_FAULT_RETURN_TYPE; +#else +typedef int VM_FAULT_RETURN_TYPE; +#endif + +/** + * _udmabuf_device_vma_fault() - udmabuf device vm area fault operation. + * @vma: Pointer to the vm area structure. + * @vfm: Pointer to the vm fault structure. + * Return: VM_FAULT_RETURN_TYPE (Success(=0) or error status(!=0)). + */ +static inline VM_FAULT_RETURN_TYPE _udmabuf_device_vma_fault(struct vm_area_struct* vma, struct vm_fault* vmf) +{ + struct udmabuf_device_data* this = vma->vm_private_data; + unsigned long offset = vmf->pgoff << PAGE_SHIFT; + unsigned long phys_addr = this->phys_addr + offset; + unsigned long page_frame_num = phys_addr >> PAGE_SHIFT; + unsigned long request_size = 1 << PAGE_SHIFT; + unsigned long available_size = this->alloc_size -offset; + unsigned long virt_addr; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) + virt_addr = vmf->address; +#else + virt_addr = (unsigned long)vmf->virtual_address; +#endif + + if (UDMABUF_DEBUG_CHECK(this, debug_vma)) + dev_info(this->dma_dev, + "vma_fault(virt_addr=%pad, phys_addr=%pad)\n", &virt_addr, &phys_addr + ); + + if (request_size > available_size) + return VM_FAULT_SIGBUS; + + if (!pfn_valid(page_frame_num)) + return VM_FAULT_SIGBUS; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)) + return vmf_insert_pfn(vma, virt_addr, page_frame_num); +#else + { + int err = vm_insert_pfn(vma, virt_addr, page_frame_num); + if (err == -ENOMEM) + return VM_FAULT_OOM; + if (err < 0 && err != -EBUSY) + return VM_FAULT_SIGBUS; + + return VM_FAULT_NOPAGE; + } +#endif +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)) +/** + * udmabuf_device_vma_fault() - udmabuf device vm area fault operation. + * @vfm: Pointer to the vm fault structure. + * Return: VM_FAULT_RETURN_TYPE (Success(=0) or error status(!=0)). + */ +static VM_FAULT_RETURN_TYPE udmabuf_device_vma_fault(struct vm_fault* vmf) +{ + return _udmabuf_device_vma_fault(vmf->vma, vmf); +} +#else +/** + * udmabuf_device_vma_fault() - udmabuf device vm area fault operation. + * @vma: Pointer to the vm area structure. + * @vfm: Pointer to the vm fault structure. + * Return: VM_FAULT_RETURN_TYPE (Success(=0) or error status(!=0)). + */ +static VM_FAULT_RETURN_TYPE udmabuf_device_vma_fault(struct vm_area_struct* vma, struct vm_fault* vmf) +{ + return _udmabuf_device_vma_fault(vma, vmf); +} +#endif + +/** + * udmabuf device vm operation table. + */ +static const struct vm_operations_struct udmabuf_device_vm_ops = { + .open = udmabuf_device_vma_open , + .close = udmabuf_device_vma_close, + .fault = udmabuf_device_vma_fault, +}; + +#endif /* #if (USE_VMA_FAULT == 1) */ + +/** + * DOC: Udmabuf Device File Operations + * + * This section defines the operation of the udmabuf device file. + * + * * udmabuf_device_file_open() - udmabuf device file open operation. + * * udmabuf_device_file_release() - udmabuf device file release operation. + * * udmabuf_device_file_mmap() - udmabuf device file memory map operation. + * * udmabuf_device_file_read() - udmabuf device file read operation. + * * udmabuf_device_file_write() - udmabuf device file write operation. + * * udmabuf_device_file_llseek() - udmabuf device file llseek operation. + * * udmabuf_device_file_ops - udmabuf device file operation table. + */ + +/** + * udmabuf_device_file_open() - udmabuf device file open operation. + * @inode: Pointer to the inode structure of this device. + * @file: to the file structure. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_device_file_open(struct inode *inode, struct file *file) +{ + struct udmabuf_device_data* this; + int status = 0; + + this = container_of(inode->i_cdev, struct udmabuf_device_data, cdev); + file->private_data = this; + this->is_open = 1; + + return status; +} + +/** + * udmabuf_device_file_release() - udmabuf device file release operation. + * @inode: Pointer to the inode structure of this device. + * @file: Pointer to the file structure. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_device_file_release(struct inode *inode, struct file *file) +{ + struct udmabuf_device_data* this = file->private_data; + + this->is_open = 0; + + return 0; +} + +/** + * _PGPROT_NONCACHED : vm_page_prot value when ((sync_mode & SYNC_MODE_MASK) == SYNC_MODE_NONCACHED ) + * _PGPROT_WRITECOMBINE : vm_page_prot value when ((sync_mode & SYNC_MODE_MASK) == SYNC_MODE_WRITECOMBINE) + * _PGPROT_DMACOHERENT : vm_page_prot value when ((sync_mode & SYNC_MODE_MASK) == SYNC_MODE_DMACOHERENT ) + */ +#if defined(CONFIG_ARM) +#define _PGPROT_NONCACHED(vm_page_prot) pgprot_noncached(vm_page_prot) +#define _PGPROT_WRITECOMBINE(vm_page_prot) pgprot_writecombine(vm_page_prot) +#define _PGPROT_DMACOHERENT(vm_page_prot) pgprot_dmacoherent(vm_page_prot) +#elif defined(CONFIG_ARM64) +#define _PGPROT_NONCACHED(vm_page_prot) pgprot_noncached(vm_page_prot) +#define _PGPROT_WRITECOMBINE(vm_page_prot) pgprot_writecombine(vm_page_prot) +#define _PGPROT_DMACOHERENT(vm_page_prot) pgprot_writecombine(vm_page_prot) +#else +#define _PGPROT_NONCACHED(vm_page_prot) pgprot_noncached(vm_page_prot) +#define _PGPROT_WRITECOMBINE(vm_page_prot) pgprot_writecombine(vm_page_prot) +#define _PGPROT_DMACOHERENT(vm_page_prot) pgprot_writecombine(vm_page_prot) +#endif + +/** + * udmabuf_device_file_mmap() - udmabuf device file memory map operation. + * @file: Pointer to the file structure. + * @vma: Pointer to the vm area structure. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_device_file_mmap(struct file *file, struct vm_area_struct* vma) +{ + struct udmabuf_device_data* this = file->private_data; + + if (vma->vm_pgoff + vma_pages(vma) > (this->alloc_size >> PAGE_SHIFT)) + return -EINVAL; + + if ((file->f_flags & O_SYNC) | (this->sync_mode & SYNC_ALWAYS)) { + switch (this->sync_mode & SYNC_MODE_MASK) { + case SYNC_MODE_NONCACHED : + vma->vm_flags |= VM_IO; + vma->vm_page_prot = _PGPROT_NONCACHED(vma->vm_page_prot); + break; + case SYNC_MODE_WRITECOMBINE : + vma->vm_flags |= VM_IO; + vma->vm_page_prot = _PGPROT_WRITECOMBINE(vma->vm_page_prot); + break; + case SYNC_MODE_DMACOHERENT : + vma->vm_flags |= VM_IO; + vma->vm_page_prot = _PGPROT_DMACOHERENT(vma->vm_page_prot); + break; + default : + break; + } + } + vma->vm_private_data = this; + +#if (USE_VMA_FAULT == 1) + { + unsigned long page_frame_num = (this->phys_addr >> PAGE_SHIFT) + vma->vm_pgoff; + if (pfn_valid(page_frame_num)) { + vma->vm_flags |= VM_PFNMAP; + vma->vm_ops = &udmabuf_device_vm_ops; + udmabuf_device_vma_open(vma); + return 0; + } + } +#endif + + return dma_mmap_coherent(this->dma_dev, vma, this->virt_addr, this->phys_addr, this->alloc_size); +} + +/** + * udmabuf_device_file_read() - udmabuf device file read operation. + * @file: Pointer to the file structure. + * @buff: Pointer to the user buffer. + * @count: The number of bytes to be read. + * @ppos: Pointer to the offset value. + * Return: Transferd size. + */ +static ssize_t udmabuf_device_file_read(struct file* file, char __user* buff, size_t count, loff_t* ppos) +{ + struct udmabuf_device_data* this = file->private_data; + int result = 0; + size_t xfer_size; + size_t remain_size; + dma_addr_t phys_addr; + void* virt_addr; + + if (mutex_lock_interruptible(&this->sem)) + return -ERESTARTSYS; + + if (*ppos >= this->size) { + result = 0; + goto return_unlock; + } + + phys_addr = this->phys_addr + *ppos; + virt_addr = this->virt_addr + *ppos; + xfer_size = (*ppos + count >= this->size) ? this->size - *ppos : count; + + if ((file->f_flags & O_SYNC) | (this->sync_mode & SYNC_ALWAYS)) + dma_sync_single_for_cpu(this->dma_dev, phys_addr, xfer_size, DMA_FROM_DEVICE); + + if ((remain_size = copy_to_user(buff, virt_addr, xfer_size)) != 0) { + result = 0; + goto return_unlock; + } + + if ((file->f_flags & O_SYNC) | (this->sync_mode & SYNC_ALWAYS)) + dma_sync_single_for_device(this->dma_dev, phys_addr, xfer_size, DMA_FROM_DEVICE); + + *ppos += xfer_size; + result = xfer_size; + return_unlock: + mutex_unlock(&this->sem); + return result; +} + +/** + * udmabuf_device_file_write() - udmabuf device file write operation. + * @file: Pointer to the file structure. + * @buff: Pointer to the user buffer. + * @count: The number of bytes to be written. + * @ppos: Pointer to the offset value + * Return: Transferd size. + */ +static ssize_t udmabuf_device_file_write(struct file* file, const char __user* buff, size_t count, loff_t* ppos) +{ + struct udmabuf_device_data* this = file->private_data; + int result = 0; + size_t xfer_size; + size_t remain_size; + dma_addr_t phys_addr; + void* virt_addr; + + if (mutex_lock_interruptible(&this->sem)) + return -ERESTARTSYS; + + if (*ppos >= this->size) { + result = 0; + goto return_unlock; + } + + phys_addr = this->phys_addr + *ppos; + virt_addr = this->virt_addr + *ppos; + xfer_size = (*ppos + count >= this->size) ? this->size - *ppos : count; + + if ((file->f_flags & O_SYNC) | (this->sync_mode & SYNC_ALWAYS)) + dma_sync_single_for_cpu(this->dma_dev, phys_addr, xfer_size, DMA_TO_DEVICE); + + if ((remain_size = copy_from_user(virt_addr, buff, xfer_size)) != 0) { + result = 0; + goto return_unlock; + } + + if ((file->f_flags & O_SYNC) | (this->sync_mode & SYNC_ALWAYS)) + dma_sync_single_for_device(this->dma_dev, phys_addr, xfer_size, DMA_TO_DEVICE); + + *ppos += xfer_size; + result = xfer_size; + return_unlock: + mutex_unlock(&this->sem); + return result; +} + +/** + * udmabuf_device_file_llseek() - udmabuf device file llseek operation. + * @file: Pointer to the file structure. + * @offset: File offset to seek. + * @whence: Type of seek. + * Return: The new position. + */ +static loff_t udmabuf_device_file_llseek(struct file* file, loff_t offset, int whence) +{ + struct udmabuf_device_data* this = file->private_data; + loff_t new_pos; + + switch (whence) { + case 0 : /* SEEK_SET */ + new_pos = offset; + break; + case 1 : /* SEEK_CUR */ + new_pos = file->f_pos + offset; + break; + case 2 : /* SEEK_END */ + new_pos = this->size + offset; + break; + default: + return -EINVAL; + } + if (new_pos < 0 ){return -EINVAL;} + if (new_pos > this->size){return -EINVAL;} + file->f_pos = new_pos; + return new_pos; +} + +/** + * udmabuf device file operation table. + */ +static const struct file_operations udmabuf_device_file_ops = { + .owner = THIS_MODULE, + .open = udmabuf_device_file_open, + .release = udmabuf_device_file_release, + .mmap = udmabuf_device_file_mmap, + .read = udmabuf_device_file_read, + .write = udmabuf_device_file_write, + .llseek = udmabuf_device_file_llseek, +}; + +/** + * DOC: Udmabuf Device Data Operations + * + * This section defines the operation of udmabuf device data. + * + * * udmabuf_device_ida - Udmabuf Device Minor Number allocator variable. + * * udmabuf_device_number - Udmabuf Device Major Number. + * * udmabuf_device_create() - Create udmabuf device data. + * * udmabuf_device_setup() - Setup the udmabuf device data. + * * udmabuf_device_info() - Print infomation the udmabuf device data. + * * udmabuf_device_destroy() - Destroy the udmabuf device data. + */ +static DEFINE_IDA(udmabuf_device_ida); +static dev_t udmabuf_device_number = 0; + +/** + * udmabuf_device_create() - Create udmabuf device data. + * @name: device name or NULL. + * @parent: parent device or NULL. + * @minor: minor_number or -1 or -2. + * Return: Pointer to the udmabuf device data or NULL. + */ +static struct udmabuf_device_data* udmabuf_device_create(const char* name, struct device* parent, int minor) +{ + struct udmabuf_device_data* this = NULL; + unsigned int done = 0; + const unsigned int DONE_ALLOC_MINOR = (1 << 0); + const unsigned int DONE_CHRDEV_ADD = (1 << 1); + const unsigned int DONE_DEVICE_CREATE = (1 << 3); + const unsigned int DONE_SET_DMA_DEV = (1 << 4); + /* + * allocate device minor number + */ + { + if ((0 <= minor) && (minor < DEVICE_MAX_NUM)) { + if (ida_simple_get(&udmabuf_device_ida, minor, minor+1, GFP_KERNEL) < 0) { + printk(KERN_ERR "couldn't allocate minor number(=%d).\n", minor); + goto failed; + } + } else if(minor < 0) { + if ((minor = ida_simple_get(&udmabuf_device_ida, 0, DEVICE_MAX_NUM, GFP_KERNEL)) < 0) { + printk(KERN_ERR "couldn't allocate new minor number. return=%d.\n", minor); + goto failed; + } + } else { + printk(KERN_ERR "invalid minor number(=%d), valid range is 0 to %d\n", minor, DEVICE_MAX_NUM-1); + goto failed; + } + done |= DONE_ALLOC_MINOR; + } + /* + * create (udmabuf_device_data*) this. + */ + { + this = kzalloc(sizeof(*this), GFP_KERNEL); + if (IS_ERR_OR_NULL(this)) { + int retval = PTR_ERR(this); + this = NULL; + printk(KERN_ERR "kzalloc() failed. return=%d\n", retval); + goto failed; + } + } + /* + * set device_number + */ + { + this->device_number = MKDEV(MAJOR(udmabuf_device_number), minor); + } + /* + * register /sys/class/udmabuf/<name> + */ + { + if (name == NULL) { + this->sys_dev = device_create(udmabuf_sys_class, + parent, + this->device_number, + (void *)this, + DEVICE_NAME_FORMAT, MINOR(this->device_number)); + } else { + this->sys_dev = device_create(udmabuf_sys_class, + parent, + this->device_number, + (void *)this, + "%s", name); + } + if (IS_ERR_OR_NULL(this->sys_dev)) { + int retval = PTR_ERR(this->sys_dev); + this->sys_dev = NULL; + printk(KERN_ERR "device_create() failed. return=%d\n", retval); + goto failed; + } + done |= DONE_DEVICE_CREATE; + } + /* + * add chrdev. + */ + { + int retval; + cdev_init(&this->cdev, &udmabuf_device_file_ops); + this->cdev.owner = THIS_MODULE; + if ((retval = cdev_add(&this->cdev, this->device_number, 1)) != 0) { + printk(KERN_ERR "cdev_add() failed. return=%d\n", retval); + goto failed; + } + done |= DONE_CHRDEV_ADD; + } + /* + * set dma_dev + */ + { + if (parent != NULL) + this->dma_dev = get_device(parent); + else + this->dma_dev = get_device(this->sys_dev); + /* + * set this->dma_dev->dma_mask + */ + if (this->dma_dev->dma_mask == NULL) { + this->dma_dev->dma_mask = &this->dma_dev->coherent_dma_mask; + } + /* + * set *this->dma_dev->dma_mask and this->dma_dev->coherent_dma_mask + * Executing dma_set_mask_and_coherent() before of_dma_configure() may fail. + * Because dma_set_mask_and_coherent() will fail unless dev->dma_ops is set. + * When dma_set_mask_and_coherent() fails, it is forcefuly setting the dma-mask value. + */ + if (*this->dma_dev->dma_mask == 0) { + int retval = dma_set_mask_and_coherent(this->dma_dev, DMA_BIT_MASK(dma_mask_bit)); + if (retval != 0) { + printk(KERN_WARNING "dma_set_mask_and_coherent(DMA_BIT_MASK(%d)) failed. return=(%d)\n", dma_mask_bit, retval); + *this->dma_dev->dma_mask = DMA_BIT_MASK(dma_mask_bit); + this->dma_dev->coherent_dma_mask = DMA_BIT_MASK(dma_mask_bit); + } + } + done |= DONE_SET_DMA_DEV; + } + /* + * initialize other variables. + */ + { + this->size = 0; + this->alloc_size = 0; + this->sync_mode = SYNC_MODE_NONCACHED; + this->sync_offset = 0; + this->sync_size = 0; + this->sync_direction = 0; + this->sync_owner = 0; + this->sync_for_cpu = 0; + this->sync_for_device = 0; + } +#if (USE_OF_RESERVED_MEM == 1) + { + this->of_reserved_mem = 0; + } +#endif +#if ((UDMABUF_DEBUG == 1) && (USE_VMA_FAULT == 1)) + { + this->debug_vma = 0; + } +#endif + mutex_init(&this->sem); + + return this; + + failed: + if (done & DONE_SET_DMA_DEV ) { put_device(this->dma_dev);} + if (done & DONE_CHRDEV_ADD ) { cdev_del(&this->cdev); } + if (done & DONE_DEVICE_CREATE) { device_destroy(udmabuf_sys_class, this->device_number);} + if (done & DONE_ALLOC_MINOR ) { ida_simple_remove(&udmabuf_device_ida, minor);} + if (this != NULL) { kfree(this); } + return NULL; +} + +/** + * udmabuf_device_setup() - Setup the udmabuf device data. + * @this: Pointer to the udmabuf device data. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_device_setup(struct udmabuf_device_data* this) +{ + if (!this) + return -ENODEV; + /* + * setup buffer size and allocation size + */ + this->alloc_size = ((this->size + ((1 << PAGE_SHIFT) - 1)) >> PAGE_SHIFT) << PAGE_SHIFT; + /* + * dma buffer allocation + */ + this->virt_addr = dma_alloc_coherent(this->dma_dev, this->alloc_size, &this->phys_addr, GFP_KERNEL); + if (IS_ERR_OR_NULL(this->virt_addr)) { + int retval = PTR_ERR(this->virt_addr); + printk(KERN_ERR "dma_alloc_coherent(size=%zu) failed. return(%d)\n", this->alloc_size, retval); + this->virt_addr = NULL; + return (retval == 0) ? -ENOMEM : retval; + } + return 0; +} + +/** + * udmabuf_device_info() - Print infomation the udmabuf device data structure. + * @this: Pointer to the udmabuf device data structure. + */ +static void udmabuf_device_info(struct udmabuf_device_data* this) +{ + dev_info(this->sys_dev, "driver version = %s\n" , DRIVER_VERSION); + dev_info(this->sys_dev, "major number = %d\n" , MAJOR(this->device_number)); + dev_info(this->sys_dev, "minor number = %d\n" , MINOR(this->device_number)); + dev_info(this->sys_dev, "phys address = %pad\n", &this->phys_addr); + dev_info(this->sys_dev, "buffer size = %zu\n" , this->alloc_size); + if (DMA_INFO_ENABLE) { + dev_info(this->sys_dev, "dma device = %s\n" , dev_name(this->dma_dev)); +#if defined(IS_DMA_COHERENT) + dev_info(this->sys_dev, "dma coherent = %d\n" , IS_DMA_COHERENT(this->dma_dev)); +#endif + dev_info(this->sys_dev, "dma mask = 0x%016llx\n", dma_get_mask(this->dma_dev)); + } +} + +/** + * udmabuf_device_destroy() - Destroy the udmabuf device data. + * @this: Pointer to the udmabuf device data. + * Return: Success(=0) or error status(<0). + * + * Unregister the device after releasing the resources. + */ +static int udmabuf_device_destroy(struct udmabuf_device_data* this) +{ + if (!this) + return -ENODEV; + + if (this->virt_addr != NULL) { + dma_free_coherent(this->dma_dev, this->alloc_size, this->virt_addr, this->phys_addr); + this->virt_addr = NULL; + } + put_device(this->dma_dev); + cdev_del(&this->cdev); + device_destroy(udmabuf_sys_class, this->device_number); + ida_simple_remove(&udmabuf_device_ida, MINOR(this->device_number)); + kfree(this); + return 0; +} + +/** + * DOC: Udmabuf Platform Device. + * + * This section defines the udmabuf platform device list. + * + * * struct udmabuf_platform_device - udmabuf platform device structure. + * * udmabuf_platform_device_list - list of udmabuf platform device structure. + * * udmabuf_platform_device_sem - semaphore of udmabuf platform device list. + * * udmabuf_platform_device_create() - Create udmabuf platform device and add to list. + * * udmabuf_platform_device_remove() - Remove udmabuf platform device and delete from list. + * * udmabuf_platform_device_remove_all() - Remove all udmabuf platform devices and clear list. + * * udmabuf_platform_device_search() - Search udmabuf platform device from list by name or number. + * * udmabuf_get_device_name_property() - Get "device-name" property from udmabuf device. + * * udmabuf_get_size_property() - Get "buffer-size" property from udmabuf device. + * * udmabuf_get_minor_number_property() - Get "minor-number" property from udmabuf device. + */ + +#if (USE_DEV_PROPERTY != 0) +#include <linux/property.h> +#endif + +/** + * struct udmabuf_platform_device - udmabuf platform device structure. + */ +struct udmabuf_platform_device { + struct device* dev; +#if (USE_DEV_PROPERTY == 0) + const char* device_name; + u32 minor_number; + u64 buffer_size; +#endif + struct list_head list; +}; + +/** + * udmabuf_platform_device_list - list of udmabuf static device structure. + * udmabuf_platform_device_sem - semaphore of udmabuf platform device list. + */ +static struct list_head udmabuf_platform_device_list; +static struct mutex udmabuf_platform_device_sem; + +/** + * udmabuf_get_device_name_property() - Get "device-name" property from udmabuf device. + * @dev: handle to the device structure. + * @name: address of device name. + * @lock: use mutex_lock()/mutex_unlock() + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_get_device_name_property(struct device *dev, const char** name, bool lock) +{ +#if (USE_DEV_PROPERTY == 0) + int status = -1; + struct udmabuf_platform_device* plat; + + if (lock) + mutex_lock(&udmabuf_platform_device_sem); + list_for_each_entry(plat, &udmabuf_platform_device_list, list) { + if (plat->dev == dev) { + if (plat->device_name == NULL) { + status = -1; + } else { + *name = plat->device_name; + status = 0; + } + break; + } + } + if (lock) + mutex_unlock(&udmabuf_platform_device_sem); + return status; +#else + return device_property_read_string(dev, "device-name", name); +#endif +} + +/** + * udmabuf_get_size_property() - Get "buffer-size" property from udmabuf device. + * @dev: handle to the device structure. + * @value: address of buffer size value. + * @lock: use mutex_lock()/mutex_unlock() + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_get_size_property(struct device *dev, u64* value, bool lock) +{ +#if (USE_DEV_PROPERTY == 0) + int status = -1; + struct udmabuf_platform_device* plat; + + if (lock) + mutex_lock(&udmabuf_platform_device_sem); + list_for_each_entry(plat, &udmabuf_platform_device_list, list) { + if (plat->dev == dev) { + *value = plat->buffer_size; + status = 0; + break; + } + } + if (lock) + mutex_unlock(&udmabuf_platform_device_sem); + return status; +#else + return device_property_read_u64(dev, "size", value); +#endif +} + +/** + * udmabuf_get_minor_number_property() - Get "minor-number" property from udmabuf device. + * @dev: handle to the device structure. + * @value: address of minor number value. + * @lock: use mutex_lock()/mutex_unlock() + * Return: Success(=0) or error status(<0). + */ + +static int udmabuf_get_minor_number_property(struct device *dev, u32* value, bool lock) +{ +#if (USE_DEV_PROPERTY == 0) + int status = -1; + struct udmabuf_platform_device* plat; + + if (lock) + mutex_lock(&udmabuf_platform_device_sem); + list_for_each_entry(plat, &udmabuf_platform_device_list, list) { + if (plat->dev == dev) { + *value = plat->minor_number; + status = 0; + break; + } + } + if (lock) + mutex_unlock(&udmabuf_platform_device_sem); + return status; +#else + return device_property_read_u32(dev, "minor-number", value); +#endif +} + +/** + * udmabuf_platform_device_search() - Search udmabuf platform device from list by name or number. + * @name: device name or NULL. + * @id: device id. + * Return: Pointer to the udmabuf_platform_device or NULL. + */ +static struct udmabuf_platform_device* udmabuf_platform_device_search(const char* name, int id) +{ + struct udmabuf_platform_device* plat; + struct udmabuf_platform_device* found_plat = NULL; + mutex_lock(&udmabuf_platform_device_sem); + list_for_each_entry(plat, &udmabuf_platform_device_list, list) { + bool found_by_name = true; + bool found_by_id = true; + if (name != NULL) { + const char* device_name; + found_by_name = false; + if (udmabuf_get_device_name_property(plat->dev, &device_name, false) == 0) + if (strcmp(name, device_name) == 0) + found_by_name = true; + } + if (id >= 0) { + u32 minor_number; + found_by_id = false; + if (udmabuf_get_minor_number_property(plat->dev, &minor_number, false) == 0) + if (id == minor_number) + found_by_id = true; + } + if ((found_by_name == true) && (found_by_id == true)) + found_plat = plat; + } + mutex_unlock(&udmabuf_platform_device_sem); + return found_plat; +} + +/** + * udmabuf_platform_device_create() - Create udmabuf platform device and add to list. + * @name: device name or NULL. + * @id: device id. + * @size: buffer size. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_platform_device_create(const char* name, int id, unsigned int size) +{ + struct platform_device* pdev = NULL; + struct udmabuf_platform_device* plat = NULL; + int retval = 0; + bool list_added = false; + + if (size == 0) + return -EINVAL; + + pdev = platform_device_alloc(DRIVER_NAME, id); + if (IS_ERR_OR_NULL(pdev)) { + retval = PTR_ERR(pdev); + pdev = NULL; + printk(KERN_ERR "platform_device_alloc(%s,%d) failed. return=%d\n", DRIVER_NAME, id, retval); + goto failed; + } + + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; + + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(dma_mask_bit); + *pdev->dev.dma_mask = DMA_BIT_MASK(dma_mask_bit); + + plat = kzalloc(sizeof(*plat), GFP_KERNEL); + if (IS_ERR_OR_NULL(plat)) { + retval = PTR_ERR(plat); + plat = NULL; + dev_err(&pdev->dev, "kzalloc() failed. return=%d\n", retval); + goto failed; + } + +#if (USE_DEV_PROPERTY == 0) + { + plat->device_name = (name != NULL) ? kstrdup(name, GFP_KERNEL) : NULL; + plat->minor_number = id; + plat->buffer_size = size; + } +#else + { + struct property_entry props_list[] = { + PROPERTY_ENTRY_STRING("device-name" , name), + PROPERTY_ENTRY_U64( "size" , size), + PROPERTY_ENTRY_U32( "minor-number", id ), + {}, + }; + struct property_entry* props = (name != NULL) ? &props_list[0] : &props_list[1]; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) + { + retval = device_add_properties(&pdev->dev, props); + if (retval != 0) { + dev_err(&pdev->dev, "device_add_properties failed. return=%d\n", retval); + goto failed; + } + } +#else + { + const struct property_set pset = { + .properties = props, + }; + retval = device_add_property_set(&pdev->dev, &pset); + if (retval != 0) { + dev_err(&pdev->dev, "device_add_propertiy_set failed. return=%d\n", retval); + goto failed; + } + } +#endif + } +#endif + + plat->dev = &pdev->dev; + mutex_lock(&udmabuf_platform_device_sem); + list_add_tail(&plat->list, &udmabuf_platform_device_list); + list_added = true; + mutex_unlock(&udmabuf_platform_device_sem); + + retval = platform_device_add(pdev); + if (retval != 0) { + dev_err(&pdev->dev, "platform_device_add failed. return=%d\n", retval); + goto failed; + } + + return 0; + + failed: + if (list_added == true) { + mutex_lock(&udmabuf_platform_device_sem); + list_del(&plat->list); + mutex_unlock(&udmabuf_platform_device_sem); + } + if (pdev != NULL) { + platform_device_put(pdev); + } + if (plat != NULL) { +#if (USE_DEV_PROPERTY == 0) + if (plat->device_name != NULL) + kfree(plat->device_name); +#endif + kfree(plat); + } + return retval; +} + +/** + * udmabuf_platform_device_remove() - Remove udmabuf platform device and delete from list. + * @plat: udmabuf_platform_device* + */ +static void udmabuf_platform_device_remove(struct udmabuf_platform_device* plat) +{ + struct device* dev = plat->dev; + struct platform_device* pdev = to_platform_device(dev); + platform_device_del(pdev); + platform_device_put(pdev); + mutex_lock(&udmabuf_platform_device_sem); + list_del(&plat->list); + mutex_unlock(&udmabuf_platform_device_sem); +#if (USE_DEV_PROPERTY == 0) + if (plat->device_name != NULL) + kfree(plat->device_name); +#endif + kfree(plat); +} + +/** + * udmabuf_platform_device_remove_all() - Remove all udmabuf platform devices and clear list. + */ +static void udmabuf_platform_device_remove_all(void) +{ + while(!list_empty(&udmabuf_platform_device_list)) { + struct udmabuf_platform_device* plat = list_first_entry(&udmabuf_platform_device_list, typeof(*(plat)), list); + udmabuf_platform_device_remove(plat); + } +} + +/** + * DOC: Udmabuf Static Devices. + * + * This section defines the udmabuf device to be created with arguments when loaded + * into ther kernel with insmod. + * + */ +#define DEFINE_UDMABUF_STATIC_DEVICE_PARAM(__num) \ + static int udmabuf ## __num = 0; \ + module_param( udmabuf ## __num, int, S_IRUGO); \ + MODULE_PARM_DESC(udmabuf ## __num, DRIVER_NAME #__num " buffer size"); + +#define CALL_UDMABUF_STATIC_DEVICE_CREATE(__num) \ + if (udmabuf ## __num != 0) { \ + ida_simple_remove(&udmabuf_device_ida, __num); \ + udmabuf_platform_device_create(NULL, __num, udmabuf ## __num); \ + } + +#define CALL_UDMABUF_STATIC_DEVICE_RESERVE_MINOR_NUMBER(__num) \ + if (udmabuf ## __num != 0) { \ + ida_simple_get(&udmabuf_device_ida, __num, __num+1, GFP_KERNEL); \ + } + +DEFINE_UDMABUF_STATIC_DEVICE_PARAM(0); +DEFINE_UDMABUF_STATIC_DEVICE_PARAM(1); +DEFINE_UDMABUF_STATIC_DEVICE_PARAM(2); +DEFINE_UDMABUF_STATIC_DEVICE_PARAM(3); +DEFINE_UDMABUF_STATIC_DEVICE_PARAM(4); +DEFINE_UDMABUF_STATIC_DEVICE_PARAM(5); +DEFINE_UDMABUF_STATIC_DEVICE_PARAM(6); +DEFINE_UDMABUF_STATIC_DEVICE_PARAM(7); + +/** + * udmabuf_static_device_reserve_minor_number_all() - Reserve udmabuf static device's minor-number. + */ +static void udmabuf_static_device_reserve_minor_number_all(void) +{ + CALL_UDMABUF_STATIC_DEVICE_RESERVE_MINOR_NUMBER(0); + CALL_UDMABUF_STATIC_DEVICE_RESERVE_MINOR_NUMBER(1); + CALL_UDMABUF_STATIC_DEVICE_RESERVE_MINOR_NUMBER(2); + CALL_UDMABUF_STATIC_DEVICE_RESERVE_MINOR_NUMBER(3); + CALL_UDMABUF_STATIC_DEVICE_RESERVE_MINOR_NUMBER(4); + CALL_UDMABUF_STATIC_DEVICE_RESERVE_MINOR_NUMBER(5); + CALL_UDMABUF_STATIC_DEVICE_RESERVE_MINOR_NUMBER(6); + CALL_UDMABUF_STATIC_DEVICE_RESERVE_MINOR_NUMBER(7); +} + +/** + * udmabuf_static_device_create_all() - Create udmabuf static devices. + */ +static void udmabuf_static_device_create_all(void) +{ + CALL_UDMABUF_STATIC_DEVICE_CREATE(0); + CALL_UDMABUF_STATIC_DEVICE_CREATE(1); + CALL_UDMABUF_STATIC_DEVICE_CREATE(2); + CALL_UDMABUF_STATIC_DEVICE_CREATE(3); + CALL_UDMABUF_STATIC_DEVICE_CREATE(4); + CALL_UDMABUF_STATIC_DEVICE_CREATE(5); + CALL_UDMABUF_STATIC_DEVICE_CREATE(6); + CALL_UDMABUF_STATIC_DEVICE_CREATE(7); +} + +/** + * DOC: Udmabuf Device Driver probe/remove section. + * + * This section defines the udmabuf device driver. + * + * * udmabuf_device_probe() - Probe call for the device driver. + * * udmabuf_device_remove() - Remove call for the device driver. + */ + +/** + * udmabuf_device_remove() - Remove udmabuf device driver. + * @dev: handle to the device structure. + * @devdata Pointer to the udmabuf device data structure. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_device_remove(struct device *dev, struct udmabuf_device_data *devdata) +{ + int retval = 0; + + if (devdata != NULL) { +#if (USE_OF_RESERVED_MEM == 1) + bool of_reserved_mem = devdata->of_reserved_mem; +#endif + retval = udmabuf_device_destroy(devdata); + dev_set_drvdata(dev, NULL); +#if (USE_OF_RESERVED_MEM == 1) + if (of_reserved_mem) { + of_reserved_mem_device_release(dev); + } +#endif + } else { + retval = -ENODEV; + } + return retval; +} + +/** + * of_property_read_ulong() - Find and read a unsigned long intger from a property. + * @node: device node which the property value is to be read. + * @propname: name of property to be searched. + * @out_value: pointer to return value, modified only if return value is 0. + * Return: Success(=0) or error status(<0). + */ +static int of_property_read_ulong(const struct device_node* node, const char* propname, u64* out_value) +{ + u32 u32_value; + u64 u64_value; + int retval; + + if ((retval = of_property_read_u64(node, propname, &u64_value)) == 0) { + *out_value = u64_value; + return 0; + } + + if ((retval = of_property_read_u32(node, propname, &u32_value)) == 0) { + *out_value = (u64)u32_value; + return 0; + } + + return retval; +} + +/** + * udmabuf_device_probe() - Probe call for the device. + * @dev: handle to the device structure. + * Return: Success(=0) or error status(<0). + * + * It does all the memory allocation and registration for the device. + */ +static int udmabuf_device_probe(struct device *dev) +{ + int retval = 0; + int prop_status = 0; + u32 u32_value = 0; + u64 u64_value = 0; + size_t size = 0; + int minor_number = -1; + struct udmabuf_device_data* device_data = NULL; + const char* device_name = NULL; + + /* + * size property + */ + if ((prop_status = udmabuf_get_size_property(dev, &u64_value, true)) == 0) { + size = u64_value; + } else if ((prop_status = of_property_read_ulong(dev->of_node, "size", &u64_value)) == 0) { + size = u64_value; + } else { + dev_err(dev, "invalid property size. status=%d\n", prop_status); + retval = -ENODEV; + goto failed; + } + if (size <= 0) { + dev_err(dev, "invalid size, size=%zu\n", size); + retval = -ENODEV; + goto failed; + } + /* + * minor-number property + */ + if (udmabuf_get_minor_number_property(dev, &u32_value, true) == 0) { + minor_number = u32_value; + } else if (of_property_read_u32(dev->of_node, "minor-number", &u32_value) == 0) { + minor_number = u32_value; + } else { + minor_number = -1; + } + /* + * device-name property + */ + if (udmabuf_get_device_name_property(dev, &device_name, true) != 0) + device_name = of_get_property(dev->of_node, "device-name", NULL); + if (IS_ERR_OR_NULL(device_name)) { + if (minor_number < 0) + device_name = dev_name(dev); + else + device_name = NULL; + } + /* + * udmabuf_device_create() + */ + device_data = udmabuf_device_create(device_name, dev, minor_number); + if (IS_ERR_OR_NULL(device_data)) { + retval = PTR_ERR(device_data); + dev_err(dev, "driver create failed. return=%d\n", retval); + device_data = NULL; + retval = (retval == 0) ? -EINVAL : retval; + goto failed; + } + dev_set_drvdata(dev, device_data); + /* + * set size + */ + device_data->size = size; + /* + * dma-mask property + * If you want to set dma-mask, do it before of_dma_configure(). + * Because of_dma_configure() needs the value of dev->coherent_dma_mask. + * However, executing dma_set_mask_and_coherent() before of_dma_configure() may fail. + * Because dma_set_mask_and_coherent() will fail unless dev->dma_ops is set. + * When dma_set_mask_and_coherent() fails, it is forcefuly setting the dma-mask value. + */ + if (of_property_read_u32(dev->of_node, "dma-mask", &u32_value) == 0) { + if ((u32_value > 64) || (u32_value < 12)) { + dev_err(dev, "invalid dma-mask property value=%d\n", u32_value); + goto failed; + } + retval = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(u32_value)); + if (retval != 0) { + dev_info(dev, "dma_set_mask_and_coherent(DMA_BIT_MASK(%d)) failed. return=%d\n", u32_value, retval); + retval = 0; + *dev->dma_mask = DMA_BIT_MASK(u32_value); + dev->coherent_dma_mask = DMA_BIT_MASK(u32_value); + } + } + /* + * of_reserved_mem_device_init() + */ +#if (USE_OF_RESERVED_MEM == 1) + if (dev->of_node != NULL) { + retval = of_reserved_mem_device_init(dev); + if (retval == 0) { + device_data->of_reserved_mem = 1; + } else if (retval != -ENODEV) { + dev_err(dev, "of_reserved_mem_device_init failed. return=%d\n", retval); + goto failed; + } + } +#endif +#if (USE_OF_DMA_CONFIG == 1) + /* + * of_dma_configure() + * - set pdev->dev->dma_mask + * - set pdev->dev->coherent_dma_mask + * - call of_dma_is_coherent() + * - call arch_setup_dma_ops() + */ +#if ((USE_OF_RESERVED_MEM == 1) && (LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0))) + /* + * Under less than Linux Kernel 5.1, if "memory-region" property is specified, + * of_dma_configure() will not be executed. + * Because in that case, it is already executed in of_reserved_mem_device_init(). + */ + if (device_data->of_reserved_mem == 0) +#endif + { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)) + retval = of_dma_configure(dev, dev->of_node, true); +#else + retval = of_dma_configure(dev, dev->of_node); +#endif + if (retval != 0) { + dev_err(dev, "of_dma_configure failed. return=%d\n", retval); + goto failed; + } +#else + of_dma_configure(dev, dev->of_node); +#endif + } +#endif + /* + * sync-mode property + */ + if (of_property_read_u32(dev->of_node, "sync-mode", &u32_value) == 0) { + if ((u32_value < SYNC_MODE_MIN) || (u32_value > SYNC_MODE_MAX)) { + dev_err(dev, "invalid sync-mode property value=%d\n", u32_value); + goto failed; + } + device_data->sync_mode &= ~SYNC_MODE_MASK; + device_data->sync_mode |= (int)u32_value; + } + /* + * sync-always property + */ + if (of_property_read_bool(dev->of_node, "sync-always")) { + device_data->sync_mode |= SYNC_ALWAYS; + } + /* + * sync-direction property + */ + if (of_property_read_u32(dev->of_node, "sync-direction", &u32_value) == 0) { + if (u32_value > 2) { + dev_err(dev, "invalid sync-direction property value=%d\n", u32_value); + goto failed; + } + device_data->sync_direction = (int)u32_value; + } + /* + * sync-offset property + */ + if (of_property_read_ulong(dev->of_node, "sync-offset", &u64_value) == 0) { + if (u64_value >= device_data->size) { + dev_err(dev, "invalid sync-offset property value=%llu\n", u64_value); + goto failed; + } + device_data->sync_offset = (int)u64_value; + } + /* + * sync-size property + */ + if (of_property_read_ulong(dev->of_node, "sync-size", &u64_value) == 0) { + if (device_data->sync_offset + u64_value > device_data->size) { + dev_err(dev, "invalid sync-size property value=%llu\n", u64_value); + goto failed; + } + device_data->sync_size = (size_t)u64_value; + } else { + device_data->sync_size = device_data->size; + } + /* + * udmabuf_device_setup() + */ + retval = udmabuf_device_setup(device_data); + if (retval) { + dev_err(dev, "driver setup failed. return=%d\n", retval); + goto failed; + } + + if (info_enable) { + udmabuf_device_info(device_data); + } + + return 0; + +failed: + udmabuf_device_remove(dev, device_data); + + return retval; +} + +/** + * DOC: Udmabuf Platform Driver + * + * This section defines the udmabuf platform driver. + * + * * udmabuf_platform_driver_probe() - Probe call for the device. + * * udmabuf_platform_driver_remove() - Remove call for the device. + * * udmabuf_of_match - Open Firmware Device Identifier Matching Table. + * * udmabuf_platform_driver - Platform Driver Structure. + */ + +/** + * udmabuf_platform_driver_probe() - Probe call for the device. + * @pdev: Handle to the platform device structure. + * Return: Success(=0) or error status(<0). + * + * It does all the memory allocation and registration for the device. + */ +static int udmabuf_platform_driver_probe(struct platform_device *pdev) +{ + int retval = 0; + + dev_dbg(&pdev->dev, "driver probe start.\n"); + + retval = udmabuf_device_probe(&pdev->dev); + + if (info_enable) { + dev_info(&pdev->dev, "driver installed.\n"); + } + return retval; +} +/** + * udmabuf_platform_driver_remove() - Remove call for the device. + * @pdev: Handle to the platform device structure. + * Return: Success(=0) or error status(<0). + * + * Unregister the device after releasing the resources. + */ +static int udmabuf_platform_driver_remove(struct platform_device *pdev) +{ + struct udmabuf_device_data* this = dev_get_drvdata(&pdev->dev); + int retval = 0; + + dev_dbg(&pdev->dev, "driver remove start.\n"); + + retval = udmabuf_device_remove(&pdev->dev, this); + + if (info_enable) { + dev_info(&pdev->dev, "driver removed.\n"); + } + return retval; +} + +/** + * Open Firmware Device Identifier Matching Table + */ +static struct of_device_id udmabuf_of_match[] = { + { .compatible = "ikwzm,u-dma-buf", }, + { /* end of table */} +}; +MODULE_DEVICE_TABLE(of, udmabuf_of_match); + +/** + * Platform Driver Structure + */ +static struct platform_driver udmabuf_platform_driver = { + .probe = udmabuf_platform_driver_probe, + .remove = udmabuf_platform_driver_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = udmabuf_of_match, + }, +}; + +/** + * DOC: udmabuf manager device + * + * * enum udmabuf_manager_state - udmabuf manager state enumeration. + * * struct udmabuf_manager_data - udmabuf manager data structure. + * * udmabuf_manager_state_clear() - udmabuf manager state clear. + * * udmabuf_manager_buffer_overflow() - udmabuf manager check buffer overflow. + * * udmabuf_manager_parse() - udmabuf manager parse buffer. + * * udmabuf_manager_file_open() - udmabuf manager file open operation. + * * udmabuf_manager_file_release() - udmabuf manager file release operation. + * * udmabuf_manager_file_write() - udmabuf manager file write operation. + * * udmabuf_manager_file_ops - udmabuf manager file operation table. + * * udmabuf_manager_device - udmabuf manager misc device structure. + * * udmabuf_manager_device_registerd - udmabuf manager device registerd flag. + * * + */ +#if (UDMABUF_MGR_ENABLE == 1) +#include <linux/miscdevice.h> + +#define UDMABUF_MGR_BUFFER_SIZE 256 + +/** + * enum udmabuf_manager_state - udmabuf manager state enumeration. + */ +enum udmabuf_manager_state { + udmabuf_manager_init_state , + udmabuf_manager_create_command, + udmabuf_manager_delete_command, + udmabuf_manager_parse_error , +}; + +/** + * struct udmabuf_manager_data - udmabuf manager data structure. + */ +struct udmabuf_manager_data { + const char* device_name; + int minor_number; + unsigned int size; + enum udmabuf_manager_state state; + unsigned int buffer_offset; + char buffer[UDMABUF_MGR_BUFFER_SIZE]; +}; + +/** + * udmabuf_manager_buffer_overflow() - udmabuf manager check buffer overflow. + * @this: Pointer to the udmabuf manager data structure. + */ +static bool udmabuf_manager_buffer_overflow(struct udmabuf_manager_data *this) +{ + if (this == NULL) + return true; + else + return (this->buffer_offset >= UDMABUF_MGR_BUFFER_SIZE); +} + +/** + * udmabuf_manager_state_clear() - udmabuf manager state clear. + * @this: Pointer to the udmabuf manager data structure. + */ +static void udmabuf_manager_state_clear(struct udmabuf_manager_data *this) +{ + this->device_name = NULL; + this->minor_number = PLATFORM_DEVID_AUTO; + this->size = 0; + this->state = udmabuf_manager_init_state; + this->buffer_offset = 0; +} + +/** + * udmabuf_manager_parse() - udmabuf manager parse buffer. + * @this: Pointer to the udmabuf manager data structure. + * @buff: Pointer to the user buffer. + * @count: The number of bytes to be written. + * Return: Size of copy from buff to this->buffer. + */ +static int udmabuf_manager_parse(struct udmabuf_manager_data *this, const char __user* buff, size_t count) +{ + bool copy_done = false; + size_t copy_size; + int parse_count; + + if (this->buffer_offset + count > UDMABUF_MGR_BUFFER_SIZE) + copy_size = UDMABUF_MGR_BUFFER_SIZE - this->buffer_offset; + else + copy_size = count; + + if (copy_from_user(&(this->buffer[this->buffer_offset]), buff, copy_size) != 0) { + return -EFAULT; + } + + parse_count = 0; + while(parse_count < copy_size) { + char* ptr = &(this->buffer[this->buffer_offset+parse_count]); + parse_count++; + if ((*ptr == '\n') || (*ptr == '\0') || (*ptr == ';')) { + *ptr = '\0'; + copy_done = true; + break; + } + } + this->buffer_offset += parse_count; + + if (copy_done == true) { + char* parse_buffer = this->buffer; + char* ptr = strsep(&parse_buffer, " "); + if (ptr == NULL) { + this->state = udmabuf_manager_parse_error; + goto failed; + } else if (strncmp(ptr, "create", strlen("create")) == 0) { + this->state = udmabuf_manager_create_command; + } else if (strncmp(ptr, "delete", strlen("delete")) == 0) { + this->state = udmabuf_manager_delete_command; + } else { + this->state = udmabuf_manager_parse_error; + goto failed; + } + ptr = strsep(&parse_buffer, " "); + if (ptr == NULL) { + this->state = udmabuf_manager_parse_error; + goto failed; + } else { + this->device_name = ptr; + } + if (this->state == udmabuf_manager_create_command) { + ptr = strsep(&parse_buffer, " "); + if (ptr == NULL) { + this->state = udmabuf_manager_parse_error; + goto failed; + } else { + u64 value; + if (kstrtoull(ptr, 0, &value) != 0) { + this->state = udmabuf_manager_parse_error; + goto failed; + } else { + this->size = value; + } + } + } + } +failed: + return parse_count; +} + +/** + * udmabuf_manager_file_open() - udmabuf manager file open operation. + * @inode: Pointer to the inode structure of this device. + * @file: to the file structure. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_manager_file_open(struct inode *inode, struct file *file) +{ + struct udmabuf_manager_data* this; + int status = 0; + + this = kzalloc(sizeof(*this), GFP_KERNEL); + if (IS_ERR_OR_NULL(this)) { + status = PTR_ERR(this); + } else { + status = 0; + udmabuf_manager_state_clear(this); + file->private_data = this; + } + return status; +} + +/** + * udmabuf_manager_file_release() - udmabuf manager file release operation. + * @inode: Pointer to the inode structure of this device. + * @file: Pointer to the file structure. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_manager_file_release(struct inode *inode, struct file *file) +{ + struct udmabuf_manager_data* this = file->private_data; + if (this != NULL) + kfree(this); + return 0; +} + +/** + * udmabuf_manager_file_write() - udmabuf manager file write operation. + * @file: Pointer to the file structure. + * @buff: Pointer to the user buffer. + * @count: The number of bytes to be written. + * @ppos: Pointer to the offset value + * Return: Transferd size. + */ +static ssize_t udmabuf_manager_file_write(struct file* file, const char __user* buff, size_t count, loff_t* ppos) +{ + struct udmabuf_manager_data* this = file->private_data; + struct udmabuf_platform_device* plat; + int result = 0; + size_t xfer_size = 0; + + if (this == NULL) + return -EINVAL; + + if (udmabuf_manager_buffer_overflow(this)) + return -ENOSPC; + + while(xfer_size < count) { + int parse_size = udmabuf_manager_parse(this, buff + xfer_size, count - xfer_size); + if (parse_size < 0) { + result = parse_size; + goto failed; + } + switch (this->state) { + case udmabuf_manager_create_command : + printk(KERN_INFO "%s : create %s %d\n" , UDMABUF_MGR_NAME, this->device_name, this->size); + result = udmabuf_platform_device_create(this->device_name, this->minor_number, this->size); + if (result == 0) { + udmabuf_manager_state_clear(this); + } else { + printk(KERN_ERR "%s : create error: %s result = %d\n", UDMABUF_MGR_NAME, this->device_name, result); + udmabuf_manager_state_clear(this); + goto failed; + } + break; + case udmabuf_manager_delete_command : + printk(KERN_INFO "%s : delete %s\n" , UDMABUF_MGR_NAME, this->device_name); + plat = udmabuf_platform_device_search(this->device_name, this->minor_number); + if (plat != NULL) { + udmabuf_platform_device_remove(plat); + udmabuf_manager_state_clear(this); + } else { + printk(KERN_ERR "%s : delete error: %s not found\n", UDMABUF_MGR_NAME, this->device_name); + udmabuf_manager_state_clear(this); + result = -EINVAL; + goto failed; + } + break; + case udmabuf_manager_parse_error : + printk(KERN_ERR "%s : parse error: ""%s""\n", UDMABUF_MGR_NAME, this->buffer); + udmabuf_manager_state_clear(this); + result = -EINVAL; + goto failed; + default: + break; + } + xfer_size += parse_size; + } + *ppos += xfer_size; + result = xfer_size; + failed: + return result; +} + +/** + * udmabuf manager file operation table. + */ +static const struct file_operations udmabuf_manager_file_ops = { + .owner = THIS_MODULE, + .open = udmabuf_manager_file_open, + .release = udmabuf_manager_file_release, + .write = udmabuf_manager_file_write, +}; + +/** + * udmabuf manager misc device structure. + */ +static struct miscdevice udmabuf_manager_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = UDMABUF_MGR_NAME, + .fops = &udmabuf_manager_file_ops, +}; + +static bool udmabuf_manager_device_registerd = false; +#endif + +/** + * DOC: u-dma-buf Kernel Module Operations + * + * * u_dma_buf_cleanup() + * * u_dma_buf_init() + * * u_dma_buf_exit() + */ + +static bool udmabuf_platform_driver_registerd = false; + +/** + * u_dma_buf_cleanup() + */ +static void u_dma_buf_cleanup(void) +{ +#if (UDMABUF_MGR_ENABLE == 1) + if (udmabuf_manager_device_registerd ){misc_deregister(&udmabuf_manager_device);} +#endif + udmabuf_platform_device_remove_all(); + if (udmabuf_platform_driver_registerd){platform_driver_unregister(&udmabuf_platform_driver);} + if (udmabuf_sys_class != NULL ){class_destroy(udmabuf_sys_class);} + if (udmabuf_device_number != 0 ){unregister_chrdev_region(udmabuf_device_number, 0);} + ida_destroy(&udmabuf_device_ida); +} + +/** + * u_dma_buf_init() + */ +static int __init u_dma_buf_init(void) +{ + int retval = 0; + + ida_init(&udmabuf_device_ida); + INIT_LIST_HEAD(&udmabuf_platform_device_list); + mutex_init(&udmabuf_platform_device_sem); + + retval = alloc_chrdev_region(&udmabuf_device_number, 0, 0, DRIVER_NAME); + if (retval != 0) { + printk(KERN_ERR "%s: couldn't allocate device major number. return=%d\n", DRIVER_NAME, retval); + udmabuf_device_number = 0; + goto failed; + } + + udmabuf_sys_class = class_create(THIS_MODULE, DRIVER_NAME); + if (IS_ERR_OR_NULL(udmabuf_sys_class)) { + retval = PTR_ERR(udmabuf_sys_class); + udmabuf_sys_class = NULL; + printk(KERN_ERR "%s: couldn't create sys class. return=%d\n", DRIVER_NAME, retval); + retval = (retval == 0) ? -ENOMEM : retval; + goto failed; + } + + udmabuf_sys_class_set_attributes(); + + udmabuf_static_device_reserve_minor_number_all(); + + retval = platform_driver_register(&udmabuf_platform_driver); + if (retval) { + printk(KERN_ERR "%s: couldn't register platform driver. return=%d\n", DRIVER_NAME, retval); + udmabuf_platform_driver_registerd = false; + goto failed; + } else { + udmabuf_platform_driver_registerd = true; + } + + udmabuf_static_device_create_all(); + +#if (UDMABUF_MGR_ENABLE == 1) + retval = misc_register(&udmabuf_manager_device); + if (retval) { + printk(KERN_ERR "%s: couldn't register udmabuf-mgr. return=%d\n", DRIVER_NAME, retval); + udmabuf_manager_device_registerd = false; + goto failed; + } else { + udmabuf_manager_device_registerd = true; + } +#endif + return 0; + + failed: + u_dma_buf_cleanup(); + return retval; +} + +/** + * u_dma_buf_exit() + */ +static void __exit u_dma_buf_exit(void) +{ + u_dma_buf_cleanup(); +} + +module_init(u_dma_buf_init); +module_exit(u_dma_buf_exit); diff --git a/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-modules/u-dma-buf/u-dma-buf.bb b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-modules/u-dma-buf/u-dma-buf.bb new file mode 100644 index 0000000000000000000000000000000000000000..5b165c4a3e04f3a0fc8c9f7a2583a84681f53696 --- /dev/null +++ b/scalp_fast_router_firmware_petalinux/project-spec/meta-user/recipes-modules/u-dma-buf/u-dma-buf.bb @@ -0,0 +1,18 @@ +SUMMARY = "Recipe for build an external u-dma-buf Linux kernel module" +SECTION = "PETALINUX/modules" +LICENSE = "GPLv2" +LIC_FILES_CHKSUM = "file://COPYING;md5=12f884d2ae1ff87c09e5b7ccc2c4ca7e" + +inherit module + +INHIBIT_PACKAGE_STRIP = "1" + +SRC_URI = "file://Makefile \ + file://u-dma-buf.c \ + file://COPYING \ + " + +S = "${WORKDIR}" + +# The inherit of module.bbclass will automatically name module packages with +# "kernel-module-" prefix as required by the oe-core build environment.