12/30/2020

Android PWM 과 GPIO 설정 및 Control Test App

1. Device Tree 관련설정 
 

Device Tree 기본문법이해 

Device Tree 기반 부팅방법

아래설정은 i.MX8에서 BSP에서 LSIO PWM 관련설정이 없어 어쩔수 없이 Datasheet를 보고 직접 만들어서 추가하였으며, MIPI에서 사용하는 PWM과 같이 i.mx27로 사용결정

$ vi *dts


/* PWM 사용을 위해서 PWM 사용과 GPIO PINMUX 설정 
iomuxc가 label 이므로  &iomuxc 사용 (iomuxc: pinctrl)  
*/

&iomuxc {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_hog>;

	/* PINMUX 를 PWM과 GPIO 변경 
	*  IMX8QXP_UART1_RX  : PWM1
	*  IMX8QXP_UART1_TX  : GPIO 
	*/
	pinctrl_lppwm1: lppwm1grp {
		fsl,pins = <
			IMX8QXP_UART1_RX_LSIO_PWM1_OUT					0x00000020
			IMX8QXP_UART1_TX_LSIO_GPIO0_IO21				0x00000020		
		>;
	};
	/* PINMUX 를 PWM과 GPIO 변경 
	*  IMX8QXP_UART1_RTS_B  : PWM2 
	*  IMX8QXP_UART1_CTS_B  : GPIO
	*/
	pinctrl_lppwm2: lppwm2grp {
		fsl,pins = <
			IMX8QXP_UART1_RTS_B_LSIO_PWM2_OUT	0x00000020		
			IMX8QXP_UART1_CTS_B_LSIO_GPIO0_IO24	0x00000020	
		>;
	};	

};

/* MIPI의 PWM 부분 삭제 전부 label이므로 &적용  */

/delete-node/ &pwm_mipi_lvds0;
/delete-node/ &pwm_mipi_lvds1;

/delete-node/ &lvds_backlight0;
/delete-node/ &lvds_backlight1;

/* UART1 과 PWM PINMUX 충돌하여 미사용로 변경  */
&lpuart1 {
    status = "disabled";
};


/* 
    LSIO PWM 관련정의가 없어서 Datasheet를 보고 각 Address에 맞춰 정의
    MIPI도 i.MX6도 PWM을 im27-pwm 사용하여 나 또한 이것으로 사용하기로 결정 
    lsio_subsys를 main으로 넣기로함 

    / { 
    .....
     }
*/

/ {
	
	lsio_subsys: bus@5d000000 {
		compatible = "simple-bus";
		#address-cells = <1>;
		#size-cells = <1>;
		ranges = <0x5d000000 0x0 0x5d000000 0x1000000>,
			<0x08000000 0x0 0x08000000 0x10000000>;

/* 
i.MXQXP의 LSIO Datasheet 관련부분을 보면 상위 Memory Map Range 영역을 설정  
현재 child's address 1 , size 1만 존재 parent address가 없음 총 4개로 memory mapped io로 하는 것으로 생각됨 (추측)
- Physical Start 0x5d000000 0x0 
- Physical End   0x5d000000+0x1000000
- Virtual  Start 0x08000000 0x0
- Virtual  End   0x08000000+0x10000000
*/

		pwm1_lsio: pwm@5d010000 {
			compatible = "fsl,imx8qxp-pwm", "fsl,imx27-pwm";
			reg = <0x5d010000 0x1000>;
			clocks = <&pwm1_lpcg 0>,
				 <&pwm1_lpcg 1>,
				 <&pwm1_lpcg 2>;				
			pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_lppwm1>;					 
			clock-names = "per", "ipg","32k";
			assigned-clocks = <&clk IMX_SC_R_PWM_1 IMX_SC_PM_CLK_PER>;
			assigned-clock-rates = <24000000>
			#pwm-cells = <2>
			power-domains = <&pd IMX_SC_R_PWM_1>;
			status = "okay";
		};	

		pwm2_lsio: pwm@5d020000 {
			compatible = "fsl,imx8qxp-pwm", "fsl,imx27-pwm";
			reg = <0x5d020000 0x1000>;
			clocks = <&pwm2_lpcg 0>,
				 <&pwm2_lpcg 1>,
				 <&pwm2_lpcg 2>;				
			pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_lppwm2>;					 
			clock-names = "per", "ipg","32k";
			assigned-clocks = <&clk IMX_SC_R_PWM_2 IMX_SC_PM_CLK_PER>;
			assigned-clock-rates = <24000000>;
			#pwm-cells = <2>;
			power-domains = <&pd IMX_SC_R_PWM_2>;
			status = "okay";
		};	

	};

//#pwm-cells   pwm 기반으로 사용하는 다른 driver에서 이를 사용  (backlight) 
https://www.kernel.org/doc/Documentation/devicetree/bindings/pwm/pwm.txt
https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings/pwm/pwm.txt

};


2. PWM Driver 기본구조분석

PWM Driver 기본구조 및 sys filesystem 사용법

  • PWM Driver (imx27)
  1. pwmchip_add()
  2. pwmchip_remove()

i.MX27 PWM Driver 


  • PWM 기반의 Driver (Back light)
  pwm_request()  기반으로 구현


2.1 sys filesystem PWM-TEST 

상위 imx27 Driver가 설정과 Clock 설정한 후 아래와 같이 설정가능하지만, 권한이 문제발생하여 아래와 같이 su 를 이용하여 root로 테스트 

  • PWM0 생성 및 기본제어방법
GPIO와 유사하게 export로 설정후 pwm0을 생성되어 자동마운트 된후 실제적인 설정진행 
  1. period : 주기를 설정 ( ns 단위) 
  2. duty_cycle: positive pulse width  (ns 단위) 
$ su 
# id
uid=0(root) gid=0(root) groups=0(root),1007(log),3009(readproc) context=u:r:su:s0

$ echo 0 > /sys/class/pwm/pwmchip0/export   // pwm0 생성됨 
$ echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/period
$ echo 500000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
$ echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable
상위 설정은 1KHz의 주기를 가지고 Duty 즉, High Pulse는 반으로 설정 

아래와 같이 50Hz 주기 설정 후 half duty로 변경 및 다양한 duty 테스트 
//50Hz 20ms 주기 설정  
$ echo 20000000 > /sys/class/pwm/pwmchip0/pwm0/period 

//10000us (10ms) (half) Positive Pulse 
$ echo 10000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle 

//1000 us (1ms)
$ echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle 

//1250 us (1.25ms) 
$ echo 1250000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle 

//1500 us (1.5ms)
$ echo 1500000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle 

//1750 us (1.75ms) 
$ echo 1750000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle 

//2000 us (2.ms)
$ echo 2000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle 

PWM 관련설정 사항 

상위는 Android의 기본 shell에서 동작않아, 이를 해결하기 위해서 init.rc 에서 export 와 권한설정하기로 결정. 



2.2 Android init.hardware.rc 문법 수정  

Linux로 말하면, init script 이며, 이는 이부분 각 service와 초기화 관련부분을 담당  
  1. init.rc 
  2. init.%hardware%.rc

  • Android Init 관련문법 복습
Init 문법을 보면 크게 Action 과 Service 와 command 나누어지며, 다음과 같이 동작한다. 
//Action 기능 이며, trigger 와 command를 다양하게 지원
on <trigger> [&& <trigger>]*
   <command>
   <command>
   <command>
   
//Service 기능이며 관련 option은 아래 링크 참조      
service <name> <pathname> [ <argument> ]*
   <option>
   <option>
   ...     

Android init 문법의 세부내용 Manual 

  • Android의 기본 init.rc 확인 
기본 init.rc 위치 및 설정확인하였지만, 직접수정하지 않는 것이 좋을 것 같아 이부분은 그대로 유지 
$ vi system/core/rootdir/init.rc  // 
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc

# Cgroups are mounted right before early-init using list from /etc/cgroups.json
on early-init
    # Disable sysrq from keyboard
    write /proc/sys/kernel/sysrq 0

    # Set the security context of /adb_keys if present.
    restorecon /adb_keys

    # Set the security context of /postinstall if present.
    restorecon /postinstall

    mkdir /acct/uid
.........    

$ cat out/target/product/mek_8q/root/init.rc   

  • i.MX8을 위한 init.hardware.rc 에 수정
각 hardware를 위한 init.rc 부분에 아래와 pwm관련설정부분을 추가를 해준다. 
상위 문법을 보면 trigger 로 조건이 맞을 경우 command로 action이 되어진다. 
$ vi ./device/fsl/imx8q/mek_8q/init.imx8qxp.rc
......
    # jhlee /sys filesystem mount가 된 경우 실행 
on property:sys.boot_completed=1
    #jhlee for PWM1/2  root 권한으로 실행됨  
    chmod 0777 /sys/class/pwm/pwmchip0
    chmod 0222 /sys/class/pwm/pwmchip0/export
    chmod 0222 /sys/class/pwm/pwmchip0/unexport
    chmod 0777 /sys/class/pwm/pwmchip1
    chmod 0222 /sys/class/pwm/pwmchip1/export
    chmod 0222 /sys/class/pwm/pwmchip1/unexport

    # PWM1 50Hz 20ms / 10ms (half)
    write /sys/class/pwm/pwmchip0/export 0
    write /sys/class/pwm/pwmchip0/pwm0/enable 1
    write /sys/class/pwm/pwmchip0/pwm0/period 20000000
    write /sys/class/pwm/pwmchip0/pwm0/duty_cycle 10000000

    # PWM2 50Hz 20ms / 10ms (half)
    write /sys/class/pwm/pwmchip1/export 0
    write /sys/class/pwm/pwmchip1/pwm0/enable 1
    write /sys/class/pwm/pwmchip1/pwm0/period 20000000
    write /sys/class/pwm/pwmchip1/pwm0/duty_cycle 10000000

    chmod 0666 /sys/class/pwm/pwmchip0/pwm0/period
    chmod 0666 /sys/class/pwm/pwmchip0/pwm0/duty_cycle
    chmod 0666 /sys/class/pwm/pwmchip0/pwm0/enable

    chmod 0666 /sys/class/pwm/pwmchip1/pwm0/period
    chmod 0666 /sys/class/pwm/pwmchip1/pwm0/duty_cycle
    chmod 0666 /sys/class/pwm/pwmchip1/pwm0/enable

    # GPIO UART1_TX 21
    write /sys/class/gpio/export 21
    write /sys/class/gpio/gpio21/direction "out"
    write /sys/class/gpio/gpio21/value 1

    # GPIO UART1_CTS_B 24
    write /sys/class/gpio/export 24
    write /sys/class/gpio/gpio24/direction "out"
    write /sys/class/gpio/gpio24/value 1

    chmod 0666 /sys/class/gpio/export
    chmod 0666 /sys/class/gpio/unexport
    chmod 0666 /sys/class/gpio/gpio21/direction
    chmod 0666 /sys/class/gpio/gpio21/value
    chmod 0666 /sys/class/gpio/gpio24/direction
    chmod 0666 /sys/class/gpio/gpio24/value
....    

$ cat out/target/product/mek_8q/vendor/etc/init/hw/init.freescale.imx8qxp.rc   // 빌드 후 실제 적용되어되었는지확인 

init.rc script이 root기반으로 동작되기 때문에 초반에 chmod를 하는것은 의미가 없다 

2.3 PWM ueventd.rc 문법 수정  

처음 착각했던 것이 export를  값을 넣어주면, uevent가 발생하여 관련부분이 pwm이 생기면서 이 script를 이용하여 permission을 변경할 줄 알았다. 

  1. ueventd.rc 
  2. ueventd.%hardware%.rc 



  • ueventd.rc 설정 
아래설정으로 하면 될 줄 알았는데, 실패했지만, 될 것이라고 생각되어 이곳에 기록하며 추후 재확인 
$ vi ./device/fsl/imx8q/mek_8q/ueventd.freescale.rc
# /sys file은 entry 5개 (주의)
# /dev file은 entry 4개 
# jhlee for pwmchip0 pwm0
/sys/devices/platform/bus@5d000000/5d010000.pwm/pwm/pwmchip0/pwm0  enable 0666 system system
/sys/devices/platform/bus@5d000000/5d010000.pwm/pwm/pwmchip0/pwm0  period 0666 system system
/sys/devices/platform/bus@5d000000/5d010000.pwm/pwm/pwmchip0/pwm0  duty_cycle 0666 system system
/sys/devices/platform/bus@5d000000/5d010000.pwm/pwm/pwmchip0/pwm0  polarity   0666 system system

# jhlee for pwmchip1 pwm0
/sys/devices/platform/bus@5d000000/5d020000.pwm/pwm/pwmchip1/pwm0  enable 0666 system system
/sys/devices/platform/bus@5d000000/5d020000.pwm/pwm/pwmchip1/pwm0  period 0666 system system
/sys/devices/platform/bus@5d000000/5d020000.pwm/pwm/pwmchip1/pwm0  duty_cycle 0666 system system
/sys/devices/platform/bus@5d000000/5d020000.pwm/pwm/pwmchip1/pwm0  polarity   0666 system system

# jhlee for pwm (0/1/2/3)
/sys/class/pwm/pwmchip*      export     0666 system system
/sys/class/pwm/pwmchip*      unexport   0666 system system
/sys/class/pwm/pwmchip*      enable     0666 system system
/sys/class/pwm/pwmchip*      period     0666 system system
/sys/class/pwm/pwmchip*      duty_cycle 0666 system system
/sys/class/pwm/pwmchip*      polarity   0666 system system
$ cat out/target/product/mek_8q/vendor/ueventd.rc  // /sys file은 entry 5개 사용 주의 
[    5.133113] ueventd: /vendor/ueventd.rc: 44: /sys/ lines must have 5 entries
[    5.140349] ueventd: /vendor/ueventd.rc: 45: /sys/ lines must have 5 entries
[    5.147472] ueventd: /vendor/ueventd.rc: 46: /sys/ lines must have 5 entries
[    5.154572] ueventd: /vendor/ueventd.rc: 47: /sys/ lines must have 5 entries

ueventd.rc pwm 과 gpio 관련예제 


ueventd 별도정리 필요 

  • android ueventd Main 소스 

  • ueventd.rc Parser 소스 
ParseConfig (ueventd.rc ) -> AddSingleLineParser->ParsePermissionsLine (각 sys or dev parsing 후 permision 설정 )
(sys 와 dev는 entry 갯수가 다르며, 이는 Cold boot를 위한 것이므로, Kernel에서 미리 설정되어야 한다)

  • ueventd의 handler 소스 
실제동작을 LOG_UEVENTS로 DEBUG 가능 

  • ueventd debug 방법 
상위 ueventd 소스를 보면 LOG_UEVENTS 설정에 따라 LOG를 볼 수 있으며 이를 강제로 선언하여 Debug를 한다. 

$ vi ./system/core/init/Android.mk 
.......
init_options += -DLOG_UEVENTS=1 \
    -DSEPOLICY_VERSION=$(POLICYVERS)
.......      


ueventd의 selinux 설정 


PWM의 sysfs의 구조 


  • root 권한으로  shell 에서 pwm 위치 찾기 

$ for i in /sys/class/pwm*/pwmchip*/pwm* ; do ls -l $i; cat $i; done
total 0
-r--r--r-- 1 root root 4096 2021-01-20 07:16 capture
-r--r--r-- 1 root root 4096 2021-01-20 07:16 consumers
-rw-rw-rw- 1 root root 4096 2021-01-20 03:33 duty_cycle
-rw-rw-rw- 1 root root 4096 2021-01-20 03:33 enable
-r--r--r-- 1 root root 4096 2021-01-20 07:16 output_type
-rw-rw-rw- 1 root root 4096 2021-01-20 03:33 period
-rw-r--r-- 1 root root 4096 2021-01-20 07:16 polarity
drwxr-xr-x 2 root root    0 2021-01-20 07:16 power
-r--r--r-- 1 root root 4096 2021-01-20 07:16 suppliers
-rw-r--r-- 1 root root 4096 2021-01-20 07:16 uevent
cat: /sys/class/pwm/pwmchip0/pwm0: Is a directory
total 0
-r--r--r-- 1 root root 4096 2021-01-20 07:17 capture
-r--r--r-- 1 root root 4096 2021-01-20 07:17 consumers
-rw-rw-rw- 1 root root 4096 2021-01-20 03:33 duty_cycle
-rw-rw-rw- 1 root root 4096 2021-01-20 03:33 enable
-r--r--r-- 1 root root 4096 2021-01-20 07:17 output_type
-rw-rw-rw- 1 root root 4096 2021-01-20 03:33 period
-rw-r--r-- 1 root root 4096 2021-01-20 07:17 polarity
drwxr-xr-x 2 root root    0 2021-01-20 07:17 power
-r--r--r-- 1 root root 4096 2021-01-20 07:17 suppliers
-rw-r--r-- 1 root root 4096 2021-01-20 07:17 uevent
cat: /sys/class/pwm/pwmchip1/pwm0: Is a directory

$ ls -alh /sys/class/pwm/          //ueventd.*.rc에 추가하 위해서 정확한 PATH 알기 (아래는 강제로 권한변경된 상태)
...
lrwxrwxrwx  1 root root 0 2020-12-30 06:06 pwmchip0 -> ../../devices/platform/bus@5d000000/5d010000.pwm/pwm/pwmchip0
lrwxrwxrwx  1 root root 0 2020-12-30 06:06 pwmchip1 -> ../../devices/platform/bus@5d000000/5d020000.pwm/pwm/pwmchip1


2.4 SELinux 

  • i.MX8의 의 SElinux 문제발생 (동작안됨)
상위에서 write /sys/class/pwm/pwmchip0/export 0 할 경우 uevent가 발생할것이며, 아래와 같이 ls를 했을 경우 access 문제발생 

mek_8q:/sys/class/pwm/pwmchip0 $ ls -alh
drwxr-xr-x 3 root root    0 1970-01-01 00:00 .[  175.487816] type=1400 audit(1609308473.916:27): avc: denied { getattr } for comm="ls" path="/sys/devices/platform/bus@5d000000/5d020000.pwm/pwm/pwmchip0/uevent" dev="sysfs" ino=34845 scontext=u:r:shell:s0 tcontext=u:object_r:sysfs:s0 tclass=file permissive=1

  • BoardConfig.mk 관련수정
SElinux 를 허용모드(permissive) 강제로 변경후 별도의 추가 *.te를 할 필요가 없으므로 이를 간단히 해결 
$ find . -name BoardConfig.mk  // 기본위치파악 
device/Vendor/ARCH/BoardName/BoardConfig.mk

$ vi ./device/fsl/imx8q/mek_8q/BoardConfig.mk    // i.MX8 관련수정 

# NXP의 기본 Hardware 설정 
# NXP default config
BOARD_KERNEL_CMDLINE := init=/init androidboot.hardware=freescale firmware_class.path=/vendor/firmware loop.max_part=7

#
# permissive 추가 
BOARD_KERNEL_CMDLINE += androidboot.selinux=permissive

상위와 같이 permissive 로 변경할 경우, 상위와 같이 error는 발생하며, 경고는 하지만, 문제가 되지 않음 
하지만, 이는 개발시에는 사용해야하면, 제품 출하시에는 반드시 관련부분을 수정해야한다. 

Android SELinux (일반 Linux의 SELinux의 사용법과 좀 다름)

SELinux 관련부분 정리 

3. Android SDK/NDK/PDK 구분 

  • SDK
Software Development Kit 로 순수 Application 입장이라고 생각하면 되겠으며, Java기반으로 Android Studio에서 제작가능하며, Google 제공하는 SDK에서 개발가능 

Google에서 공식적인 제공하는 SDK 설치 한 후 이와 관련된 API 를 이용하여 사용자가 원할 경우 Custom으로 제작도 가능한 것으로 파악된다. 

  • NDK
Native Development Kit로 주로 C/C++로 기반으로 구현하며, JNI /HAL 을 제공하며,  Library만 구성을 하여도 Application에서 이를 가져다가 사용가능하다. 
Google에서 제공하는 NDK를 설치 한 후 이를 Build를 진행해야한다. 
 
  • PDK
Product(Platform) Development Kit로  SDK 와 NDK 의 동시에 사용할 경우를 말한다. 
가장 쉬운예제가 JNI기능을 포함 SDK Program이며, 이는 PDK라고 불리운다. 
상위에서 제공한 SDK 와 NDK를 설치한 후 이를 기반으로 작성가능하다. 


NDK/PDK기반의 예제 

상위를 간단히 분석을하면, Target의 /system/lib 에 본인이 구현한 library를 넣고 , 이를 기반으로 SDK에서 Class를 Loading하여 사용하면된다. 

Android Studio를 이용하거나, ASOP에서 직접 빌드를 하면된다. 


3.1 PWM PDK 작성방법 

Android Studio에서 Native C++ 로 기반으로 PDK 구현할 경우 문제는 SELinux의 domain 이 untrusted_app로 생성되며, 이를 허용하지 않아 
상위처럼 강제로 허용모드로 변경해야 했지만, Vendor입장에서 상위 untruested_app의 domain을 변경해서 사용해야 할 것 같다. 

  • Android Source 소스 수정 
  1. ASOP_SETTING : ASOP에서 직접 수정(Access 문제) 후 이를 Build 후 관련 Image 생성 
    상위 2.2 init.hardware.rc 관련부분 수정 (init.xxxx.rc 상위 이름 참조 chmod로 변경)
    상위 2.4 SELinux 의 Permissive , 즉 허용모드 변경   ( 접근허용으로 변경)

  • Android Studio 기반으로  Native C++  생성 
  1. Android Studio-> New -> Native C++ 선택 후 기본 생성 
  2. cpp -> CMakeLists.txt  변경 (기존 예제인 C++ 삭제)
  3. cpp -> GpioCtr.c / GpioCtr.h 추가 
  4. cpp -> PWMCtr.c / PWMCtr.h 추가 (예제인 C++ 삭제)
  5. java -> com.example.jhlee -> GpioCtr.java
  6. java -> com.example.jhlee -> PWMCtr.java
  7. java -> com.example.jhlee -> MainActivity.java

  • 관련 Sample 소스 



3.3 기타사항 


Android Application의 경우 AndroidManifest.xml 까지 추후에 봐야한다.

아래의 소스는 NDK 기반에서 /system/lib 에 넣고 SDK에서 이를 호출하여 동작하는 방식 



Anroid Thing Native

Android Thing 상용화 무료가 아님



상위 /sys/class/pwm/pwmchip0 권한문제

PWM- Back light