USB는 여러 종류의 장치를 지원한다. 이 다양한 장치를 기능별로 분류하여 class라고 부르고, class 별로 표준 protocol을 정의하여 놓았는데 각 장치는 이 protocol를 준수하여 동작하여야 한다.

USB Software stack은 다음과 같다.

 HID (Human Interface Device) - mouse, keyboard과 같은 저속 시리얼을 위한 Class
 CDC (Communication Device Class) - 시리얼 통신에 주로 이용
 MSC (Mass-Storage Class) - 대용량 저장 장치를 위한 것으로 USB Flash 같은 곳에 사용
 ACM (Abstract Control Model Class) - V-port와 같은 추상화된 구성에 주로 사용

이렇게 준비된 Class Driver를 사용하지 않고 사용자 정의 방식으로 동작이 가능하다.

의료 쪽의 PHCD (Personal Healthcare Device Class) 는 운동용 시계, 혈압 측정기, 체온계, 체중계, 혈당 측정기 등과 같은 의료 기기가 호스트에 접속하여 개인과 피트니스 코치 또는 환자와 의사 사이의 의사소통을 간편하게 하도록 지원한다.

USB는 master/slave라는 표현 대신 Host/Device라는 용어를 사용한다. Host가 Master에 해당되고, Device가 slave이다.
PC가 Host이고 Embedded Device가 Device로 동작할 때, 아래처럼 구성된다.

 

CDC (Communication Device Class)
 USB to Serial, USB to ethernet 등 USB 포트에 연결하여 통신하는 디바이스들이 CDC를 주로 사용한다. CDC는 통신 방법에 따라 ACM, ECM, EEM, NCM, OBEX 등등 다양한 subclass 들이 있다.

ACM (Abstract Control Model Class)
 USB to Serial 에 주로 사용되는 subclass 이다.

ECM (Ethernet Networking Control Model)
 USB to ethernet의 subclass 이다. ECM이 발표된 이후, ECM의 비효율적인 부분을 개선하여 발표되고 있다. EEM (Ethernet Emulation Model), NCM(Network Control Model), MBIM(Mobile Broadband Interface Model) 등은 모두 ethernet packet을 전송하기 위한 class 이다.

 

USB Host를 개발하려면 당연하게 USB Host Controller Driver가 있어야 한다. Host Controller는 OHCI, UHCI, EHCI and xHCI Controller가 있고, 이중 EHCI가 USB 2.0 high speed 까지 지원되는 것으로, 가장 널리 사용된다. 하지만, 대부분은 USB Device 개발이고, 이 경우엔 Host에서 돌아가는 class driver를 개발해야 하고 USB Device 에서 돌아가는 제어블럭이 필요하게 된다. 

 

출처: m.blog.naver.com/PostView.nhn?blogId=msnayana&logNo=220148672836&proxyReferer=https:%2F%2Fwww.google.com%2F

'USB' 카테고리의 다른 글

[USB] 호스트가 디바이스 정보 얻기  (0) 2021.01.17
[USB] Window USB Descriptor  (0) 2020.11.01

 

 USB 디바이스는 firmware 레벨에서 descriptor에 정보를 보관한다. 운영체제는 USB 디바이스와의 통신 채널인 control endpoint를 통해서 디바이스에 descriptor 제공을 요청하고, 요청을 받은 디바이스는 descriptor를 제공한다.

 USB Specification 2.0을 지원하려면 디바이스, 인터페이스, 엔드포인터에 대한 정보를 담고 있는 standard descriptor를 지원해야 한다. 디바이스는 제조사 별 class descriptor와 vendor-specific descriptor가 있다.

MS의 descriptorstandard USB string descriptor (OS string descriptor), OS feature descriptor가 있다. OS string Descriptor는 OS feature descriptor를 얻기 위해 필요한 Descriptor이다.

디바이스의 OS feature descriptor를 얻기 위해서 Windows는
 1. 디바이스의 OS string descriptor를 조회하기 위한 제어 요청을 디바이스에게 보낸다.
 2. 수신한 디바이스 OS string descriptor가 Microsoft가 정의한 포맷을 준수하는지 확인한다.
 3. 디바이스의 OS feature descriptor를 조회하기 위해서 OS string descriptor의 bMS_VendorCode 필드에 있는 데이터를 사용한다.

 

MS의 OS featrue descriptor 종류로는 Extened Compat ID, Extended Properties, Genre가 있다.
 - Extened Compat ID
 Windows에는 USB 디바이스의 기본 드라이버를 결정하기 위해서 calss 와 subclass 코드를 사용한다. 새로운 타입의 USB 디바이스는 이 class 와 subclass 코드를 가지고 있지 않다. 이런 경우에 USB 디바이스 제조사는 extended compat ID라는 OS featrue descriptor 정보를 디바이스 firmware에 저장한다. Windows는 디바이스가 연결될 때 이 정보를 조회하여 디바이스에 대한 기본 드라이버를 결정하고 load한다.
 - Extended Properties
 USB 디바이스의 class level 또는 devnod level를 선언할 수 있다. USB 디바이스 제조사는 이 Extended Properties OS feature descriptor를 이용해서 도움 페이지, URL, 아이콘 같은 추가 특성 정보를 펌웨어에 저장할 수 있다.
 - Genre
 이 디스크립터는 게임 컨드롤러 같은 HID 디바이스들이 상세한 configuration data를 제공하기 위해 사용한다. 예를 들어서 전형적인 게임 컨트롤러는 컨트롤에 대한 기본 정보를 포함하는 report descriptor를 가지고 있다. 하지만 이 데이터는 게임 컨트롤러가 사용될 수 있는 여러가지 다른 상황에서 컨트롤과 액션 간의 최적의 mapping을 하기에는 부족하다. USB 디바이스 제조사는 디바이스 펌웨어에 상세한 매핑 정보를 저장하게 위해 이 descriptor를 사용할 수 있다.

 

MS의 OS String Descriptor
USB 디바이스는 스트링 인덱스 0xEE에 저장된 OS String Descriptor를 갖고 있어야 한다.
 - 이 디스크립터를 사용한다는 것은 디바이스가 하나 이상의 OS feature descriptor를 갖고 있음을 의미
 - OS featrue descriptor 조회에 필요한 데이터들을 포함
 - USB 디바이스 제조사가 0xEE에 저장할 수 있는 스트링들로부터 OS String Descriptor를 구분할 수 있게 하는 signature를 포함
 - 버전 정보 포함

 

MS의 OS Feature Descriptor
버전 1.00 에서 MS는 3개의 OS feature descriptor에 대한 표준 포맷을 정의했다. OS feature descriptor는 다음 사항을 만족해야 한다.
 - 다비이스는 최대 255개까지 descriptor를 저장할 수 있다.
 - descriptor는 디바이스 또는 특정 인터페이스 기능과 연관된 것일 수 있다.
 - 각 타입 당 한개의 OS Feature Descriptor가 허용된다. 여러 OS Feature Descriptor들이 특정 인터페이스나 function에 연관될 수 있다. 이 경우에 디바이스는 자신이 갖고 있는 인터페이스들이나 function들 만큼의 디스크립터들을 가질 수 있다. 
 - descriptor는 크기와 버전정보를 포함해야 한다.
 - descriptor 최대 크기는 특정 OS featrue descriptor에 따라 다르다. 프로토콜은 feature descriptors 크기를 16MB까지 허용하지만, 그 크기는 펌웨어 비용 때문에 제한된다.

 

출처: blog.naver.com/jskimadd/10171305013

'USB' 카테고리의 다른 글

[USB] 호스트가 디바이스 정보 얻기  (0) 2021.01.17
[USB] USB 장치  (0) 2020.11.01

안드로이드 쉘에서 input 커맨드를 통해 텍스트 입력, 터치, 키 입력 등을 할 수 있다.

 

input text

input text “insert%syour%stext”

안드로이드에서 텍스트 입력을 하는 커맨드. %s 는 space를 의미한다. 안드로이드 쉘에서 다음과 같이 입력하면 된다.

(ex) input text “Hello%sWorld”

 

input tap

input tap [xPosition] [YPosition]

안드로이드에서 짧은 터치를 하는 커맨드. 안드로이드 디버깅 모드에 진입하면 터치하는 곳의 x좌표, y좌표를 알 수 있다. 이를 토대로 안드로이드 쉘에서 이 커맨드를 치면 해당하는 좌표를 짧게 클릭한다.

(ex) input tap 500 200

 

input swipe

input swipe fromXPosition fromYPosition toXPosition toYPosition Time

안드로이드에서 처음 좌표에서 끝 좌표까지 주어진 시간동안 터치를 재현해주는 커맨드이다. 예시의 경우 (100, 500) 부터 (100,1000) 까지 100ms 동안 터치를 해준다.

(ex) input swipe 100 500 100 1000 100

 

input keyevent

input keyevent #NUM

안드로이드에서 키보드 입력을 대신 할 수 있는 커맨드이다. 필자의 경우는 터치가 안되는 안드로이드 디바이스가 안드로이드 동작이 느려서 이 키 이벤트로 안드로이드를 제어했다. 성능이 낮은 디바이스의 경우 터치하는 것보다 키 이벤트로 입력하는 것이 더 빠르다.

(ex) input keyevent 4

 

 Keyevent List

  • 0 –> “KEYCODE_0”
  • 1 –> “KEYCODE_SOFT_LEFT”
  • 2 –> “KEYCODE_SOFT_RIGHT”
  • 3 –> “KEYCODE_HOME”
  • 4 –> “KEYCODE_BACK”
  • 5 –> “KEYCODE_CALL”
  • 6 –> “KEYCODE_ENDCALL”
  • 7 –> “KEYCODE_0”
  • 8 –> “KEYCODE_1”
  • 9 –> “KEYCODE_2”
  • 10 –> “KEYCODE_3”
  • 11 –> “KEYCODE_4”
  • 12 –> “KEYCODE_5”
  • 13 –> “KEYCODE_6”
  • 14 –> “KEYCODE_7”
  • 15 –> “KEYCODE_8”
  • 16 –> “KEYCODE_9”
  • 17 –> “KEYCODE_STAR”
  • 18 –> “KEYCODE_POUND”
  • 19 –> “KEYCODE_DPAD_UP”
  • 20 –> “KEYCODE_DPAD_DOWN”
  • 21 –> “KEYCODE_DPAD_LEFT”
  • 22 –> “KEYCODE_DPAD_RIGHT”
  • 23 –> “KEYCODE_DPAD_CENTER”
  • 24 –> “KEYCODE_VOLUME_UP”
  • 25 –> “KEYCODE_VOLUME_DOWN”
  • 26 –> “KEYCODE_POWER”
  • 27 –> “KEYCODE_CAMERA”
  • 28 –> “KEYCODE_CLEAR”
  • 29 –> “KEYCODE_A”
  • 30 –> “KEYCODE_B”
  • 31 –> “KEYCODE_C”
  • 32 –> “KEYCODE_D”
  • 33 –> “KEYCODE_E”
  • 34 –> “KEYCODE_F”
  • 35 –> “KEYCODE_G”
  • 36 –> “KEYCODE_H”
  • 37 –> “KEYCODE_I”
  • 38 –> “KEYCODE_J”
  • 39 –> “KEYCODE_K”
  • 40 –> “KEYCODE_L”
  • 41 –> “KEYCODE_M”
  • 42 –> “KEYCODE_N”
  • 43 –> “KEYCODE_O”
  • 44 –> “KEYCODE_P”
  • 45 –> “KEYCODE_Q”
  • 46 –> “KEYCODE_R”
  • 47 –> “KEYCODE_S”
  • 48 –> “KEYCODE_T”
  • 49 –> “KEYCODE_U”
  • 50 –> “KEYCODE_V”
  • 51 –> “KEYCODE_W”
  • 52 –> “KEYCODE_X”
  • 53 –> “KEYCODE_Y”
  • 54 –> “KEYCODE_Z”
  • 55 –> “KEYCODE_COMMA”
  • 56 –> “KEYCODE_PERIOD”
  • 57 –> “KEYCODE_ALT_LEFT”
  • 58 –> “KEYCODE_ALT_RIGHT”
  • 59 –> “KEYCODE_SHIFT_LEFT”
  • 60 –> “KEYCODE_SHIFT_RIGHT”
  • 61 –> “KEYCODE_TAB”
  • 62 –> “KEYCODE_SPACE”
  • 63 –> “KEYCODE_SYM”
  • 64 –> “KEYCODE_EXPLORER”
  • 65 –> “KEYCODE_ENVELOPE”
  • 66 –> “KEYCODE_ENTER”
  • 67 –> “KEYCODE_DEL”
  • 68 –> “KEYCODE_GRAVE”
  • 69 –> “KEYCODE_MINUS”
  • 70 –> “KEYCODE_EQUALS”
  • 71 –> “KEYCODE_LEFT_BRACKET”
  • 72 –> “KEYCODE_RIGHT_BRACKET”
  • 73 –> “KEYCODE_BACKSLASH”
  • 74 –> “KEYCODE_SEMICOLON”
  • 75 –> “KEYCODE_APOSTROPHE”
  • 76 –> “KEYCODE_SLASH”
  • 77 –> “KEYCODE_AT”
  • 78 –> “KEYCODE_NUM”
  • 79 –> “KEYCODE_HEADSETHOOK”
  • 80 –> “KEYCODE_FOCUS”
  • 81 –> “KEYCODE_PLUS”
  • 82 –> “KEYCODE_MENU” //MK: 비번이 걸려 있지 않은 경우, 화면을 켠상태에서 해당 Keycode를 입력하면 메인 화면으로 넘어감
  • 83 –> “KEYCODE_NOTIFICATION”
  • 84 –> “KEYCODE_SEARCH”
  • 85 –> “KEYCODE_MEDIA_PLAY_PAUSE”
  • 86 –> “KEYCODE_MEDIA_STOP”
  • 87 –> “KEYCODE_MEDIA_NEXT”
  • 88 –> “KEYCODE_MEDIA_PREVIOUS”
  • 89 –> “KEYCODE_MEDIA_REWIND”
  • 90 –> “KEYCODE_MEDIA_FAST_FORWARD”
  • 91 –> “KEYCODE_MUTE”
  • 92 –> “KEYCODE_PAGE_UP”
  • 93 –> “KEYCODE_PAGE_DOWN”
  • 94 –> “KEYCODE_PICTSYMBOLS”
  • 224 –> “KEYCODE_WAKEUP” //MK: Power 버튼과 차이가 있음. 화면이 켜져 있는 상태에서 해당 키를 입력하면 화면이 그대로 켜져있음

 

 

출처

https://mkblog.co.kr/2018/09/10/android-adb-input-command/

 

[Android] ADB “input” Command 사용법 (adb shell input) – MKBlog

안드로이드 단말에서 Developer 옵션을 켜면 ADB를 사용해서 많은 것을 할 수 있다. 나의 경우 게임에서 특정 플레이를 반복적으로 해야 하는 부분을 자동으로 해보려고 찾다가 “adb shell input” Command로 구현 할 수 있어서 관련 Command를 정리하였다. Input Text Command: adb shell input text “insert%syour%stext” (%s 는 space를 의미) 설명: “insert text” C

mkblog.co.kr

 

'Android' 카테고리의 다른 글

에러 코드 <include/errno.h>  (0) 2019.04.09

Android에서 tinyalsa를 다루면서 pcm_read 에러가 나길래 찾아본 에러 코드..

ALSA 다루다가 에러 났을 때 에러 코드에 따른 대응 방법은 ALSA; Advanced Linux Sound Architecture 글 참고

<참고 웹페이지> https://android.googlesource.com/kernel/lk/+/dima/for-travis/include/errno.h

 

/*
 * Copyright (c) 2013 Corey Tabaka
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#ifndef __ERRNO_H
#define __ERRNO_H
#include <compiler.h>
__BEGIN_CDECLS
extern int *__geterrno(void);
#define errno (*__geterrno())
#define	EPERM 1		/* Not super-user */
#define	ENOENT 2	/* No such file or directory */
#define	ESRCH 3		/* No such process */
#define	EINTR 4		/* Interrupted system call */
#define	EIO 5		/* I/O error */
#define	ENXIO 6		/* No such device or address */
#define	E2BIG 7		/* Arg list too long */
#define	ENOEXEC 8	/* Exec format error */
#define	EBADF 9		/* Bad file number */
#define	ECHILD 10	/* No children */
#define	EAGAIN 11	/* No more processes */
#define	ENOMEM 12	/* Not enough core */
#define	EACCES 13	/* Permission denied */
#define	EFAULT 14	/* Bad address */
#define	ENOTBLK 15	/* Block device required */
#define	EBUSY 16	/* Mount device busy */
#define	EEXIST 17	/* File exists */
#define	EXDEV 18	/* Cross-device link */
#define	ENODEV 19	/* No such device */
#define	ENOTDIR 20	/* Not a directory */
#define	EISDIR 21	/* Is a directory */
#define	EINVAL 22	/* Invalid argument */
#define	ENFILE 23	/* Too many open files in system */
#define	EMFILE 24	/* Too many open files */
#define	ENOTTY 25	/* Not a typewriter */
#define	ETXTBSY 26	/* Text file busy */
#define	EFBIG 27	/* File too large */
#define	ENOSPC 28	/* No space left on device */
#define	ESPIPE 29	/* Illegal seek */
#define	EROFS 30	/* Read only file system */
#define	EMLINK 31	/* Too many links */
#define	EPIPE 32	/* Broken pipe */
#define	EDOM 33		/* Math arg out of domain of func */
#define	ERANGE 34	/* Math result not representable */
#define	ENOMSG 35	/* No message of desired type */
#define	EIDRM 36	/* Identifier removed */
#define	ECHRNG 37	/* Channel number out of range */
#define	EL2NSYNC 38	/* Level 2 not synchronized */
#define	EL3HLT 39	/* Level 3 halted */
#define	EL3RST 40	/* Level 3 reset */
#define	ELNRNG 41	/* Link number out of range */
#define	EUNATCH 42	/* Protocol driver not attached */
#define	ENOCSI 43	/* No CSI structure available */
#define	EL2HLT 44	/* Level 2 halted */
#define	EDEADLK 45	/* Deadlock condition */
#define	ENOLCK 46	/* No record locks available */
#define EBADE 50	/* Invalid exchange */
#define EBADR 51	/* Invalid request descriptor */
#define EXFULL 52	/* Exchange full */
#define ENOANO 53	/* No anode */
#define EBADRQC 54	/* Invalid request code */
#define EBADSLT 55	/* Invalid slot */
#define EDEADLOCK 56	/* File locking deadlock error */
#define EBFONT 57	/* Bad font file fmt */
#define ENOSTR 60	/* Device not a stream */
#define ENODATA 61	/* No data (for no delay io) */
#define ETIME 62	/* Timer expired */
#define ENOSR 63	/* Out of streams resources */
#define ENONET 64	/* Machine is not on the network */
#define ENOPKG 65	/* Package not installed */
#define EREMOTE 66	/* The object is remote */
#define ENOLINK 67	/* The link has been severed */
#define EADV 68		/* Advertise error */
#define ESRMNT 69	/* Srmount error */
#define	ECOMM 70	/* Communication error on send */
#define EPROTO 71	/* Protocol error */
#define	EMULTIHOP 74	/* Multihop attempted */
#define	ELBIN 75	/* Inode is remote (not really error) */
#define	EDOTDOT 76	/* Cross mount point (not really error) */
#define EBADMSG 77	/* Trying to read unreadable message */
#define EFTYPE 79	/* Inappropriate file type or format */
#define ENOTUNIQ 80	/* Given log. name not unique */
#define EBADFD 81	/* f.d. invalid for this operation */
#define EREMCHG 82	/* Remote address changed */
#define ELIBACC 83	/* Can't access a needed shared lib */
#define ELIBBAD 84	/* Accessing a corrupted shared lib */
#define ELIBSCN 85	/* .lib section in a.out corrupted */
#define ELIBMAX 86	/* Attempting to link in too many libs */
#define ELIBEXEC 87	/* Attempting to exec a shared library */
#define ENOSYS 88	/* Function not implemented */
#define ENMFILE 89      /* No more files */
#define ENOTEMPTY 90	/* Directory not empty */
#define ENAMETOOLONG 91	/* File or path name too long */
#define ELOOP 92	/* Too many symbolic links */
#define EOPNOTSUPP 95	/* Operation not supported on transport endpoint */
#define EPFNOSUPPORT 96 /* Protocol family not supported */
#define ECONNRESET 104  /* Connection reset by peer */
#define ENOBUFS 105	/* No buffer space available */
#define EAFNOSUPPORT 106 /* Address family not supported by protocol family */
#define EPROTOTYPE 107	/* Protocol wrong type for socket */
#define ENOTSOCK 108	/* Socket operation on non-socket */
#define ENOPROTOOPT 109	/* Protocol not available */
#define ESHUTDOWN 110	/* Can't send after socket shutdown */
#define ECONNREFUSED 111	/* Connection refused */
#define EADDRINUSE 112		/* Address already in use */
#define ECONNABORTED 113	/* Connection aborted */
#define ENETUNREACH 114		/* Network is unreachable */
#define ENETDOWN 115		/* Network interface is not configured */
#define ETIMEDOUT 116		/* Connection timed out */
#define EHOSTDOWN 117		/* Host is down */
#define EHOSTUNREACH 118	/* Host is unreachable */
#define EINPROGRESS 119		/* Connection already in progress */
#define EALREADY 120		/* Socket already connected */
#define EDESTADDRREQ 121	/* Destination address required */
#define EMSGSIZE 122		/* Message too long */
#define EPROTONOSUPPORT 123	/* Unknown protocol */
#define ESOCKTNOSUPPORT 124	/* Socket type not supported */
#define EADDRNOTAVAIL 125	/* Address not available */
#define ENETRESET 126
#define EISCONN 127		/* Socket is already connected */
#define ENOTCONN 128		/* Socket is not connected */
#define ETOOMANYREFS 129
#define EPROCLIM 130
#define EUSERS 131
#define EDQUOT 132
#define ESTALE 133
#define ENOTSUP 134		/* Not supported */
#define ENOMEDIUM 135   /* No medium (in tape drive) */
#define ENOSHARE 136    /* No such host or network path */
#define ECASECLASH 137  /* Filename exists with different case */
#define EILSEQ 138
#define EOVERFLOW 139	/* Value too large for defined data type */
#define EWOULDBLOCK EAGAIN	/* Operation would block */
#define __ELASTERROR 2000	/* Users can add values starting here */
__END_CDECLS
#endif

'Android' 카테고리의 다른 글

[안드로이드] input 커맨드  (0) 2019.05.28

ALSA는 Audio Linux Sound Architecture의 약자.

리눅스에서 Audio를 구현하고자 할 때 사용되는 공개 라이브러리이다.

많이 사용되는 ALSA와 ALSA보다 몸집이 작은 tinyALSA, 이 두 라이브러리의 기본 API에 대해 설명한다.

 

 

  • ALSA Overview
  • tinyALSA

    • Data Structure

      • PCM config
      • PCM Structure
    • PCM Open
    • PCM Read
    • PCM Write
    • PCM Close
  • ALSA
    • Data Structures
    • PCM Open
    • PCM HW Params
    • PCM SW Params
    • PCM Prepare
    • PCM Start
    • PCM Avail
    • PCM Read
    • PCM Write
    • PCM Close
  • ALSA lib 참고 웹페이지

    • ALSA
    • Tiny ALSA

 

 


ALSA Overview

 

흔히 말하는 ALSA는 보통 User Space에서 ALSA library를 가져다 쓰는 것을 말한다.

ALSA application으로는 aplay, arecode, amixer, tinyplay 등이 있으며, 오디오 어플리케이션을 쓸 때 이 코드를 많이 참고하여 사용한다.


 

 

 

tinyALSA

Android에는 tinyALSA가 올라가있다.

tinyALSA의 경우에는 단순히 Open, Read/Write, Close만 해주면 audio 사용이 가능하다.

 

 

Data Structure

tinyALSA를 사용하기 위해서는 PCM Handler와 PCM Config가 필요하다.

 

PCM config

struct pcm_config {
    unsigned int channels;
    unsigned int rate;
    unsigned int period_size;
    unsigned int period_count;
    enum pcm_format format;
    /* Values to use for the ALSA start, stop and silence thresholds.  Setting
     * any one of these values to 0 will cause the default tinyalsa values to be
     * used instead.  Tinyalsa defaults are as follows.
     *
     * start_threshold   : period_count * period_size
     * stop_threshold    : period_count * period_size
     * silence_threshold : 0
     */
    unsigned int start_threshold;
    unsigned int stop_threshold;
    unsigned int silence_threshold;
    /* Minimum number of frames available before pcm_mmap_write() will actually
     * write into the kernel buffer. Only used if the stream is opened in mmap mode
     * (pcm_open() called with PCM_MMAP flag set).   Use 0 for default.
     */
    int avail_min;
};

pcm config 구조체에서는 channel 개수, rate, period size, period count, audio format에 대한 설정을 해준다.

  • format
    • PCM_FORMAT_S16_LE
      • Signed 16-bit, Little Endian
    • 그 외 PCM_FORMAT_S16_BE, PCM_FORMAT_S24_LE, PCM_FORMAT_S24_BE
    • tinyalsa\pcm.h 에서 확인 가능
  • period size
    • 한번에 읽어오는 frame의 개수
  • period count
    • buffer = period size * period count
  • start threshold
    • PCM start 하기에 최소한으로 필요한 frame 개수
  • stop threshold
    • PCM stop 하기에 최소한으로 필요한 frame 개수

 

PCM Structure

pcm 구조체

struct pcm {
    int fd;
    unsigned int flags;
    int running:1;
    int underruns;
    unsigned int buffer_size;
    unsigned int boundary;
    char error[PCM_ERROR_MAX];
    struct pcm_config config;
    struct snd_pcm_mmap_status *mmap_status;
    struct snd_pcm_mmap_control *mmap_control;
    struct snd_pcm_sync_ptr *sync_ptr;
    void *mmap_buffer;
    unsigned int noirq_frames_per_msec;
    int wait_for_avail_min;
};

 

PCM Open

struct pcm *pcm_open(unsigned int card, unsigned int device, unsigned int flags, const struct pcm_config *config)

Description

pcm open을 하고 pcm 구조체를 받아온다.

Parameters

card - sound card 번호

device - dai-link 번호

ex) device tree에서 sound card 0의 dai-link 0번 i2S를 사용한다면, card와 device에 각각 0을 넘겨주면 된다.

flags - pcm open flags

ex) PCM_OUT, PCM_IN, PCM_MMAP, PCM_NOIRQ, PCM_NORESTART, PCM_MONOTONIC, PCM_NONBLOCK, PCM_STATE_PREPARED .. etc

config - pcm config 구조체

Return

pcm 구조체

 

PCM Read

int pcm_read(struct pcm *pcm, void *data, unsigned int count)

Description

data에 pcm read한 데이터를 count만큼 가져온다.

Parameters

pcm - open할 때 반환받은 pcm 구조체

data - audio data를 저장할 buffer

count - frame 당 byte 개수

보통 하나의 frame에 몇 byte가 있는지 반환하는 pcm_frames_to_bytes(const struct pcm *pcm, unsigned int frames) 함수를 사용해서 count 값을 넣어준다.

Returns

0일 경우, 성공

-EPIPE의 경우, underrun이나 overrun 발생할 때 반환

-ESTRPIPE 의 경우, device가 suspend 상태일 때 반환

-EBADFD의 경우, 어플리케이션과 alsa-lib 사이에 연결이 끊어진 경우에 반환

-ENOTTY, -ENODEV 의 경우, device가 물리적으로 해제된 경우에 반환

 

PCM Write

int pcm_write(struct pcm *pcm, const void *data, unsigned int count)

Description

data에 저장된 데이터를 count만큼 쓴다.

Parameters

pcm - open할 때 반환받은 pcm 구조체

data - audio data를 저장된 buffer

count - frame 당 byte 개수

보통 하나의 frame에 몇 byte가 있는지 반환하는 pcm_frames_to_bytes(const struct pcm *pcm, unsigned int frames) 함수를 사용해서 count 값을 넣어준다.

Returns

0 일 경우, 성공

-EPIPE의 경우, underrun이나 overrun 발생할 때 반환

-ESTRPIPE 의 경우, device가 suspend 상태일 때 반환

-EBADFD의 경우, 어플리케이션과 alsa-lib 사이에 연결이 끊어진 경우에 반환

-ENOTTY, -ENODEV 의 경우, device가 물리적으로 해제된 경우에 반환


PCM Close

int pcm_close(struct pcm *pcm)

Description

pcm을 close한다.

Parameters

pcm - open할 때 반환받은 pcm 구조체

Returns

항상 0 반환

 

 

 

 

 

 

ALSA

Linux에서는 ALSA를 사용한다.

ALSA의 경우에는 open, hw parameter 설정, sw parameter 설정, prepare, start, (avail 체크), read/write, close 과정으로 audio를 다룬다.

 

 

Data Structures

PCM structure

typedef struct  _snd_pcm snd_pcm_t

PCM HW config

typedef struct  _snd_pcm_hw_params snd_pcm_hw_params_t

PCM SW config

typedef struct  _snd_pcm_sw_params snd_pcm_sw_params_t

 

PCM Open

int snd_pcm_open(snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode)

Description

pcm을 open한다.

Parameters

pcmp - open할 때 반환 받을 pcm 구조체

name - PCM 식별자

(ex) "hw:0,2"

stream - playback 목적으로 pcm open을 하는지, capture 목적으로 pcm open을 하는지 넘겨줌

(ex) SND_PCM_STREAM_PLAYBACK, SND_PCM_STREAM_CAPTURE

mode - open mode

(ex) SND_PCM_NONBLOCK, SND_PCM_ASYNC

Returns

항상 0 반환

 

PCM HW Params

Hardware Parameter란 audio format, channel 개수, sampling rate, period size, buffer size 등을 말한다.

 

ALSA Library FunctionDescription

snd_pcm_hw_params_malloc snd_pcm_hw_params_t 구조체 메모리 할당
snd_pcm_hw_params_any snd_pcm_hw_params_t 구조체 초기화
snd_pcm_hw_params_set_access access type 설정 (ex) SND_PCM_ACCESS_RW_INTERLEAVED, SND_PCM_ACCESS_RW_NONINTERLEAVED
snd_pcm_hw_params_set_format audio format 설정 (ex) SND_PCM_FORMAT_S16_LE
snd_pcm_hw_params_set_channels audio channel 설정
snd_pcm_hw_params_set_buffer_size audio buffer size 설정
snd_pcm_hw_params_set_rate_near 샘플레이트 설정 시 하드웨어가 정확한 샘플레이트를 지원하지 않는다면 가장 근접한 가능한 레이트를 사용하도록 설정
snd_pcm_hw_params_set_period_size alsa period_size를 설정
snd_pcm_hw_params

snd_pcm_hw_params_t 구조체에 저장된 PCM hardware configuration을 하드웨어에 알려주고 snd_pcm_prepare를 호출

snd_pcm_hw_params_free snd_pcm_hw_params_t 구조체 free


참고 웹페이지  https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___h_w___params.html#ga6e2dd8efbb7a4084bd05e6cc458d84f7

 

 

 

<cf> interleaved access vs non-interleaved access

 

  • Interleaved mode
    • 하나의 buffer에 channel sample이 번갈아가며 저장
  • Non-interleaved mode
    • 각 채널별로 buffer가 존재하여 sample이 저장됨

 

 

 

PCM SW Params

Software Parameter란 start threshold, stop threshold, avail min 등을 말한다.

 

ALSA Library FunctionDescription

snd_pcm_sw_params_malloc snd_pcm_sw_params_t 구조체 할당
snd_pcm_sw_params_current 현재 SW config를 snd_pcm_sw_params_t 구조체에 저장
snd_pcm_sw_params_set_start_threshold start threshold 설정 (보통 period size로 설정) ※ byte 크기로 넘겨주는 것에 유의
snd_pcm_sw_params_set_avail_min 최소 avail 설정 (보통 period size으로 설정)
snd_pcm_sw_params snd_pcm_sw_params_t 구조체에 저장된 PCM software configuration을 아래 layer에 알려줌
snd_pcm_sw_params_free snd_pcm_sw_params_t 구조체 free

참고 웹페이지 https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___s_w___params.html

 

(cf) alsa에서 말하는 avail 이란, buffer에서 빈 공간 frame 개수를 말한다. delay는 buffer에 있는 데이터가 들은 frame 개수를 말한다.

 

PCM Prepare

int snd_pcm_prepare(snd_pcm_t *pcm)

Description

pcm을 prepare한다.

Parameters

pcm - open할 때 반환 받은 pcm 구조체

Returns

0 일 경우, 성공

-EPIPE의 경우, underrun이나 overrun 발생할 때 반환

-ESTRPIPE 의 경우, device가 suspend 상태일 때 반환

-EBADFD의 경우, 어플리케이션과 alsa-lib 사이에 연결이 끊어진 경우에 반환

-ENOTTY, -ENODEV 의 경우, device가 물리적으로 해제된 경우에 반환


PCM Start

int snd_pcm_start(snd_pcm_t *pcm)

Description

pcm을 start한다.

Parameters

pcm - open할 때 반환 받은 pcm 구조체

Returns

0 일 경우, 성공

-EPIPE의 경우, underrun이나 overrun 발생할 때 반환

-ESTRPIPE 의 경우, device가 suspend 상태일 때 반환

-EBADFD의 경우, 어플리케이션과 alsa-lib 사이에 연결이 끊어진 경우에 반환

-ENOTTY, -ENODEV 의 경우, device가 물리적으로 해제된 경우에 반환


PCM Avail

snd_pcm_sframes_t snd_pcm_avail(snd_pcm_t *pcm)

Description

읽히거나 쓸 준비가 된 frame 개수를 반환한다. (ALSA에서 보통 avail은 버퍼의 비어있는 공간, delay는 버퍼의 데이터가 있는 공간을 말한다.)

Parameters

pcm - open할 때 반환 받은 pcm 구조체

Returns

양수일 경우, frame 개수 반환

-EPIPE의 경우, underrun이나 overrun 발생할 때 반환

-ESTRPIPE 의 경우, device가 suspend 상태일 때 반환

-EBADFD의 경우, 어플리케이션과 alsa-lib 사이에 연결이 끊어진 경우에 반환

-ENOTTY, -ENODEV 의 경우, device가 물리적으로 해제된 경우에 반환

 

PCM Read

snd_pcm_sframes_t snd_pcm_readi(snd_pcm_t *pcm, void buffer, snd_pcm_uframes_t size)

Description

interleaved frame을 읽는다.

만일 PCM이 blocking mode로 열렸을 경우, 모든 frame이 찰 때까지 기다린다. underrun이 발생할 경우에만 읽어온 frame 개수가 적을 수 있다.

non-blocking mode일 경우, 기다리지 않는다.

Parameters

pcm - open할 때 반환 받은 pcm 구조체

buffer - 읽어오려는 frame을 담을 버퍼

size - 읽으려는 frame 개수

Returns

양수일 경우, 읽어온 frame 개수 반환

-EPIPE의 경우, underrun이나 overrun 발생할 때 반환

underrun이 발생 하는 경우, snd_pcm_prepare를 호출해야 함.

-ESTRPIPE 의 경우, device가 suspend 상태일 때 반환

suspend 상태인 경우, snd_pcm_resume을 해준 뒤 snd_pcm_prepare 호출해야 함.

-EBADFD의 경우, 올바른 state가 아닐 경우 반환

SND_PCM_STATE_PREPARED 상태나 SND_PCM_STATE_RUNNING 상태일 때만 read/write가 가능

snd_pcm_sframes_t snd_pcm_readn(snd_pcm_t *pcm, void buffer, snd_pcm_uframes_t size)

Description

non-interleaved frame을 읽는다.

만일 PCM이 blocking mode로 열렸을 경우, 모든 frame이 찰 때까지 기다린다. underrun이 발생할 경우에만 읽어온 frame 개수가 적을 수 있다.

non-blocking mode일 경우, 기다리지 않는다.

Parameters

pcm - open할 때 반환 받은 pcm 구조체

buffer - 읽어오려는 frame을 담을 버퍼

size - 읽으려는 frame 개수

Returns

양수일 경우, 읽어온 frame 개수 반환

-EPIPE의 경우, underrun이나 overrun 발생할 때 반환

underrun이 발생 하는 경우, snd_pcm_prepare를 호출해야 함.

-ESTRPIPE 의 경우, device가 suspend 상태일 때 반환

suspend 상태인 경우, snd_pcm_resume을 해준 뒤 snd_pcm_prepare 호출해야 함.

-EBADFD의 경우, 올바른 state가 아닐 경우 반환

SND_PCM_STATE_PREPARED 상태나 SND_PCM_STATE_RUNNING 상태일 때만 read/write가 가능

PCM Write

snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, void buffer, snd_pcm_uframes_t size)

Description

interleaved frame을 쓴다.

만일 PCM이 blocking mode로 열렸을 경우, 모든 frame이 play 될때까지 기다리거나 playback ring buffer에 넣을 때까지 기다린다. underrun이 발생할 경우에만 읽어온 frame 개수가 적을 수 있다.

non-blocking mode일 경우, 기다리지 않는다.

Parameters

pcm - open할 때 반환 받은 pcm 구조체

buffer - 읽어오려는 frame을 담을 버퍼

size - 읽으려는 frame 개수

Returns

양수일 경우, 읽어온 frame 개수 반환

-EPIPE의 경우, underrun이 발생할 때 반환

underrun이 발생 하는 경우, snd_pcm_prepare를 호출해야 함

-ESTRPIPE 의 경우, device가 suspend 상태일 때 반환

suspend 상태인 경우, snd_pcm_resume을 해준 뒤 snd_pcm_prepare 호출해야 함

-EBADFD의 경우, 올바른 state가 아닐 경우 반환

SND_PCM_STATE_PREPARED 상태나 SND_PCM_STATE_RUNNING 상태일 때만 read/write가 가능

 

snd_pcm_sframes_t snd_pcm_writen(snd_pcm_t *pcm, void buffer, snd_pcm_uframes_t size)

Description

non-interleaved frame을 쓴다.

만일 PCM이 blocking mode로 열렸을 경우, 모든 frame이 play 될때까지 기다리거나 playback ring buffer에 넣을 때까지 기다린다. underrun이 발생할 경우에만 읽어온 frame 개수가 적을 수 있다.

non-blocking mode일 경우, 기다리지 않는다.

Parameters

pcm - open할 때 반환 받은 pcm 구조체

buffer - 읽어오려는 frame을 담을 버퍼

size - 읽으려는 frame 개수

Returns

양수일 경우, 읽어온 frame 개수 반환

-EPIPE의 경우, underrun이 발생할 때 반환

underrun이 발생 하는 경우, snd_pcm_prepare를 호출해야 함

-ESTRPIPE 의 경우, device가 suspend 상태일 때 반환

suspend 상태인 경우, snd_pcm_resume을 해준 뒤 snd_pcm_prepare 호출해야 함

-EBADFD의 경우, 올바른 state가 아닐 경우 반환

SND_PCM_STATE_PREPARED 상태나 SND_PCM_STATE_RUNNING 상태일 때만 read/write가 가능


PCM Close

int snd_pcm_close(snd_pcm_t *pcm)

Description

pcm을 close한다.

Parameters

pcm - open할 때 반환 받은 pcm 구조체

Returns

0 일 경우, 성공

음수의 경우 실패

 

 

 

ALSA library 참고 웹페이지

ALSA

ALSA libWeb Page

PCM interface https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html#ga042aba7262a4cbb4d444b6fc08cb7124
HW params https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___h_w___params.html
SW Params https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___s_w___params.html#ga79b12cbbd309750156261e7f5a39167b

 

Tiny ALSA

ALSA libWeb Page

github https://github.com/tinyalsa/tinyalsa

 

 

디바이스 드라이버 (Device Driver) - 3. file_operations 구조체




3.1 file_operations란?


  • 문자 디바이스 드라이버와 응용 프로그램을 연결하는 고리.
  • linux/fs.h에서 정의하는 이 구조체는 함수 포인터 집합이다.
  • 특정 동작 함수를 구현하여 가리켜야한다.
  • 지정하지 않으면 NULL로 남겨두어야 한다.



3.2 file_operations 구조체 및 필드 설명



  • file_operations 구조체

struct file_operations{

       struct moduel *owner;

       ssize_t (*read) (struct file *, loff_t, int);

       ssize_t (*write) (struct file *, const char *, size_t, loff_t *);

       int (*open) (struct inode *, struct file *);

       int (*release) (struct inode *, struct file *);

       int (*mmap) (struct inode *, struct file *);


       ...


 



  • file_operations 구조체 필드 설명

- struct module *owner;
파일 오퍼레이션의 소유자를 나타낸다. 보통 <linux/module.h>에 정의되어 잇는 THIS_MODULE 매크로를 사용해 초기화 한다.

- loff (*llseek (struct file *, loff_t, int)
디바이스 드라이버의 파이 ㄹ포인터 위치를 강제로 이동시키는 함수를 지정한다.

- ssize_t (*read) (struct file *, char *, size_t, loff_t *);
디바이스에서 자료를 읽는데 사용한다. NULL 이면 -EINVAL 반환

- ssize_t (*write) (struct file *, const char *, size_t, loff_t * );
자료를 디바이스로 보낸다. NULL이면 -EINVAL 반환

- unsigned int (*poll) (struct file *, struct poll_table_struct * );
다중 입출력 처리를 가능하게 해주는 poll, epoll, select의 백엔드이다.

- int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
디바이스 관련 명령들을 제어할 수 있다.

- int (*unlocked_ioctl) (struct file *flip, unsigned int cmd, unsigned long arg);
모든 CPU에서 lock을 걸던 것을 개별적인 lock을 걸 수 있도록 ioctl에서 바꾼 함수.
함수에 진입 시 커널에 대한 락이 처리되어지지 않기 때문에 함수 내에서 별도의 동기화 기법을 사용해야 한다. 이 함수를 부르면 된다.



3.3 file_operations에 연결되는 함수



  • open 함수
디바이스 드라이버가 처음 열렸을 때 하드웨어 초기화
디바이스 드라이버의 동작에 필요한 에러 체크
- -ENODEV 하드웨어가 존재하지 않는다.
- -ENOMEM 커널 메모리가 부족하다.
- -EBUSY 디바이스가 이미 사용 중이다.
minor 번호에 대한 처리가 필요한 경우 file_operations 구조체를 갱신
- 보통 file 구조체 flip의 private_data에 등록하여 사용한다.

  • release 함수
device_close로 부르는 경우도 있다.
open이 flip->private data에 할당한 데이터의 할당 삭제
마지막 close 호출 시 디바이스 종료


  • write 함수

- 주요 매소드

 ssize_t xxx_write (struct file *flip, char *buff, size_t count, loff_t *offp)

  사용자 영역인 buff에서 count 바이트 만큼 읽은 후  디바이스의 offp 위치로 저장


*인자 설명
struct file *flip   읽기와 쓰기에 전달되는 flip은 디바이스 파일이 어떤 형식으로 열렸는가에 대한 정보를 저장
loff_t f_ops        f_pos 필드 변수에는 현재의 읽기/쓰기 위치를 저장




- 데이터 전달 함수

kernel 영역과 user 영역은 서로 접근하지 못하는 메모리 영역이기 때문에, pointer를 이용하지 못하고, 데이터를 전달하는 함수를 이용

 copy_from_user(void *to, void *from, unsigned long size)

 application으로부터 data를 복사한다.

 get_user(x, ptr)

 x 변수에 ptr의 사용자 메모리 값을 대입한다.





  • read 함수

- 주요 매소드

 ssize_t xxx_read (struct file *flip, char * buff, size_t count, loff_t *offp) 

 디바이스의 offp 위치에서 count 바이트만큼을 읽어서 사용자 영역인 buff로 저장해주는 기능



- 데이터 전달 함수

kernel 영역과 user 영역은 서로 접근하지 못하는 메모리 영역이기 때문에, pointer를 이용하지 못하고, 데이터를 전달하는 함수를 이용

copy_to_user(void *to, void *from, unsigned long size)

 application으로 data를 복사해준다.

 put_user(x, ptr)

 x 변수 값을 ptr의 사용자 메모리 값에 대입한다.





  • ioctl 함수


일반적으로 I/O Control에 관련한 작업을 수행하는 함수.

대부분의 ioctl 메소드 구현은 cmd 인수 값에 따라 올바른 동작을 선택하는 switch 문으로 구성한다.


- 사용자 영역에서 ioctl 함수 시스템 콜


int ioctl (int fd, int cmd, ...);



- 커널 영역에서 ioctl 함수


int (*unlocked_ioctl) (struct file *flip, unsiged int cmd, unsigned long arg);


- inode와 flip 포인터는 응용 프로그램의 파일 디스크립터 fd와 일치하는 인수

- cmd 인수는 명령을 나타내는 응용 프로그램의 인수 전달

- arg 인수는 명령 실행의 결과 데이터가 전달되는 unsigned long 형의 정수 또는 포인터


- cmd의 구성


총 32 bit로, 

[0:1] 읽기/쓰기 구분

[2:15] 데이터 크기

[16:23] 매직넘어

[24:31] 구분 번호



- cmd 명령의 해석 매크로 함수



 _IOC_NR

 구분 번호 필드 값을 읽는 매크로 

 _IOC_TYPE

 매직 넘버 필드 값을 읽는 매크로 

 _IOC_SIZE

 데이터의 크기 필드 값을 읽는 매크로 

 _IOC_DIR

 읽기와 쓰기 속성 필드 값을 읽는 매크로 



(예) if ( _IOC_TYPE(cmd) != MY_MAGIC ) return -EINVAL;



- cmd 명령의 작성 매크로 함수



 _IO 

 부가적인 데이터가 없는 명령을 만드는 매크로 

 _IOR

 데이터를 읽어오기 위한 명령을 작성 

 _IOW

 데이터를 써 넣기 위한 명령을 작성 

 _IOWR

 디바이스 드라이버에서 읽고 쓰기 위한 명령을 작성하는 매크로 



(예) _IOW (매직넘버, 구분번호, 정수형








디바이스 드라이버 (Device Driver) - 2. 디바이스 파일










2.1 디바이스 파일의 목적


  • Device Driver가 사용할 장치 파일을 만든다.
  • 유저가 Device Driver File을 이용해 장치를 사용할 수 있다.
  • 장치 파일을 생성하기 위해서는 mknod를 통해서 장치 파일을 생성한다.





2.2 디바이스 파일의 생성


  • mknod의 사용
- 일반적으로 /dev 경로에 생성한다.
- 장치 파일은 user와 Device Driver를 연결해주는 매개체이다.
- devfs에서 관리된다.


$ mknod /dev/name type major minor 



예) mknod /dev/SK c 252 0


  • 파일의 구성
- dentry 구조체 : 경로를 관리하는 구조체

- inode 구조체 : 파일을 관리하는 구조체

- 부모는 dentry.d_subdirs, 자식들은 dentry.d_child와 double linked list로 구성되어 있다.




2.3 디바이스 파일 동적 할당


  • udev
- 디바이스 파일을 자동으로 생성할 목적으로 만들어진 데몬

- /dev를 수동 생성 방법을 대체할 방법론
- 시스템 디바이스(sysfs)에서 정보를 취합할 API 제공

- udev는 sysfs와 밀접한 관계가 있으며, 각 디바이스의 탐지, 인식, 제거, 변경 등의 event를 커널 레벨에서 관리하기 위하여 hotplug 기능이 필수적이다.

- UNIX 계열의 device driver는 file 형태.

- kernel에 device driver가 loading하면 사용 가능 상태가 된다.

- Device driver에 사용하기 위해서는 반드시 /dev 밑에 device node file이 있어야 application이 접근 가능하다.


  • udev 탄생 배경

- /dev 밑에 디바이스 파일 노드를 만드는데 기존 방식의 문제 발생
- 너무나 많은 device node file를 만들어야 함
- kernel device model이 도입되면서 hotplug 관련 처리 문제를 해결할 수 있게 됨???


  • udev 데몬의 동작











- netlink를 통해 uevent를 udev 데몬에게 알린다.

netlink란?

커널과 유저 인터페에스 프로세스 사이에서 IPC를 하는 소켓과 비슷한(socket-like) 메커니즘 ?













캐릭터 디바이스 드라이버



1. 디바이스 타입



  • dev_t
major/minor 번호가 조합된 수형
major 번호와 쌍을 이뤄 하나의 장치 번호를 구성


- 매크로 (include/linux/kdev_t.h)

MAJOR(dev_t dev);         주번호 추출

MINOR(dev_t dev);         부번호 추출

MKDEV(int ma, int mi);    번호 설정



  • major 번호의 결정

- Manual Allocation

현재 사용 중이지 않은 major 번호를 알아낸 후 직접 하드 코딩함.

- Dynamic Allocation

alloc_chrdev_region() 함수를 이용하여 동적으로 할당

단점: 미리 디바이스 파일을 만들어 둘 수 없음
해결책: 장치 번호 등록 후 /proc/devices를 읽어 major 번호를 얻고, 장치 파일을 만드는 스크립트를 활용한다.




  • cdev 구조체

커널은 내부적으로 캐릭터 디바이스를 표현하기 위해 cdev 구조체를 사용
커널이 file operation에 등록된 디바이스 등록 함수 (open, read..)를 호출하기 전에 할당 및 등록하여야 한다.

<linux/cdev.h> 

 struct cdev{

      struct kobject kobj;

      struct module *owner;

      const struct file_operations *ops;

      struct list_head list; dev_t dev;

      unsigned int count;

}






2. 캐릭터 디바이스 드라이버 API



  • register_chrdev_region()

디바이스 개수를 포함한 디바이스 드라이버 등록 함수



- 함수


int register_chrdev_region (dev_t first, unsigned int count, char* name)



- 매개 변수


 first 

 할당 받으려는 디바이스 번호 범위 중 시작 번호.

 major/minor 번호를 모두 포함

 count

 minor 번호로 디바이스 개수

 *name

 디바이스 이름 (/proc/devices 에 나타난다)





  • alloc_chrdev_region()
디바이스 번호를 동적으로 할당하기 위한 함수

- 함수

int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name)

- 매개 변수

*dev

 성공적인 경우 디바이스 번호가 할당

 firstminor

 디바이스에 할당될 첫번째 minor number, 일반적으로 0

 count

 minor number로 디바이스 개수

 *name

 디바이스 이름(/proc/devices와 sysfs에 나타난다)





  • unregister_chrdev_region()
사용중인 디바이스 번호 해제

- 함수

void unregister_chrdev_region (dev_t first, unsigned int count)




  • cdev_init()
cdev 구조체 초기화

- 함수

void cdev_init (struct *cdev, struct file_operations *fops)

- 매개 변수

 *cdev 

 초기화할 cdev 구조체 

 *fops

 등록할 fop 구조체 포인터 





  • cdev_add()
준비된 cdev 구조체를 커널에 등록, 캐릭터 디바이스 등록

- 함수

void cdev_add (struct cdev *cdev, dev_t num, unsigned int count)

- 매개변수

 *cdev

 등록한 cdev 구조체

 num

 등록할 디바이스 번호 (major/minor 포함) 

 count

 등록할 디바이스 개수, 주로 1 






  • cdev_del()
등록된 캐릭터 디바이스를 제거.
디바이스 파일도 삭제해야 한다.
디바이스 번호도 삭제, unregister_chrdev_region() 이용

- 함수

int cdev_del (struct cdev *cdev)








디바이스 드라이버 (Device Driver) - 1. 개요


  • 시스템이 지원하는 하드웨어를 응용 프로그램에서 사용할 수 있도록 커널에서 제공하는 라이브러리
  • 응용 프로그램이 하드웨어를 제어하려면 커널에 자원을 요청하고, 커널은 요청에 따라 시스템 관리
    ..

  • 디바이스 드라이버의 역할
    • 디바이스 드라이버는 하드웨어를 사용가능하게 만들어 줄 뿐 하드웨어를 어떻게 사용할지에 대한 결정은 응용 프로그램에 넘겨야 한다.
    • 디바이스 드라이버는 온갖 상황을 고려해 균형있게 작성한다.

  • 리눅스 디바이스 드라이버의 공통적 특성
    • 커널 코드
      • 디바이스 드라이버는 커널의 한 부분이므로, 커널의 다른 코드와 마찬가지로 잘못되면 시스템에 치명적인 피해를 줄 수 있다.
    • 커널 인터페이스
      • 디바이스 드라이버는 리눅스 커널이나 자신이 속한 서브시스템에 표준 인터페이스를 제공해야 한다.
    • 커널 메커니즘과 서비스
      • 디바이스 드라이버는 메모리 할당, 인터럽트 전달, wait queue와 같은 표준 커널 서비스를 사용할 수 있다.
    • Loadable
      • 대부분의 리눅스 디바이스 드라이버는 커널 모듈로서, 필요할 때 로드하고 더 이상 필요하지 않을 때 언로드 할 수 있다.
    • 설정 가능 (Configuration)
      • 리눅스 디바이스 드라이버를 커널에 포함하여 컴파일할 수 있다.
      • 어떤 장치를 넣을 것인지는 커널을 compile할 대 설정할 수 있다.


  • 디바이스 드라이버 종류
    • 문자 디바이스 드라이버
      • 임의의 길이를 갖는 문자열이나 자료의 순차성을 지닌 장치를 다루는 디바이스 드라이버
      • serial, console, keyboard, printer, mouse 등이 있다.
    • 블록 디바이스 드라이버
      • 일정 크기의 버퍼(블록 단위)를 통해 데이터를 처리하는 디바이스
      • 커널 내부의 파일시스템에서 관리하고 내부적인 버퍼가 있는 디바이스 드라이버
      • 하드 디스크, 램 디스크 등이 있다.
    • 네트워크 디바이스 드라이버
      • 네트워크 계층과 연결되어 네트워크 통신을 통해 네트워크 패킷을 송수신할 수 있는 기능을 제공
      • 이더넷, PPP 등이 있다.


커널 모듈


  • 리눅스 커널이 부팅되어 동작중인 상태에서 디바이스 드라이버를 동적으로 추가하거나 제거할 수 있게 하는 개념.
  • 리눅스는 다른 운영체제와 달리 대부분의 기능을 모듈로 구현할 수 있다.
  • 커널의 한 부분으로 동작한다.

  • 모듈 프로그래밍
    • 모듈은 커널의 프로그램의 특징을 갖고 있으면서 커널에 등록, 해제할 수 있으므로 일반 프로그램과는 다른 소스 형식
    • 커널 라이브러리를 객체 형태(.ko)로 만들어서 시스템 콜을 통해서 리눅스 커널에 등록 요청 -> 커널은 해당 객체를 커널에 동적으로 링크
    • 커널 심볼 테이블
      • 커널 내부의 함수나 변수 중 외부에서 참조할 수 있는 함수의 심볼과 주소를 담은 테이블
      • 객체 형태로 작성된 커널 모듈참조할 커널 내부의 함수나 변수에 연결되어 동적으로 링크된다.
      • 코드에서 EXPORT_SYMBOL 매크로로 심볼 등록을 하면 보드의 /proc/kallsyms에서 등록된 심볼을 확인할 수 있다.


    • 커널 내 심볼의 시작 주소 / 심볼 타입 / 심볼 이름


  • 커널 모듈 vs 응용 프로그램

    • 응용 프로그램

      • exit으로 종료시 문제가 될 경우 시스템에 큰 영향을 미치지 않음 (kill)

      • 시스템콜을 사용하지 않고 API(Application Programming Interface)를 사용
        • UNIX에서는 POSIX을 표준으로 사용
        • POSIX: IEEE에서 OS를 위한 표준 인터페이스 규칙 ex) stdio.h, string.h
    • 커널 모듈
      • 등록 후 초기화 함수를 실행한 뒤 요청에만 응답
      • exit는 더 이상 요청을 받아들이지 않겠다는 의미
      • 유저 공간의 응용 프로그램이 직접 커널 코드를 호출하는 것은 불가능
      • 커널은 보호된 메모리 공간에 있으므로 시스템이 커널 모드로 전환되어야 한다.

    • 커널 코드와 유저 코드의 차이점
      • 커널은 커널 안에 구현된 라이브러리를 사용해야 한다.
            ex) lib/string.c -> linux/string.h
      • 커널은 GNU C로 작성된다.
      • 커널에는 유저 공간에서와 같은 메모리 보호가 쉽지 않다.
      • 커널은 고정 크기의 스택을 갖는다.


    • module를 사용하기 위한 명령
      • modutils 패키지에 포함된 명령
      • 명령
        설명
        insmod 모듈명.ko

        시스템에 모듈을 추가

        rmmod 모듈명

        시스템에서 모듈을 제거

        lsmod

        현재 등록된 모듈을 확인

 

  • 모듈의 기본 형태
    • 초기화 함수 
      • static int my_init(void);
    • 초기화 등록 매크로 함수
      • module_init(my_init);
      • 초기화 함수는 외부 코드에 의해 불려지지 않는다.
      • 초기화가 성공하면 0을 리턴한다.
      • 초기화는 대게 리소스를 등록하거나 자료구조를 생성한다.

  • 커널 모듈 적재 절차
    1. 주어진 모듈명을 ".ko" 인 파일에서 모듈명을 얻는다.
    2. /lib/modules/ 의 하부 디렉토리에서 해당 파일명을 찾는다.
    3. 읽어온 파일의 코드와 모듈명 그리고 module 구조체를 저장하는데 필요한 메모리 영역의 크기를 구한다.
    4. 구조체의 정보를 이용해 create_module() 함수를 호출한다.
    5. 이 함수는 모듈의 권한을 검사하고, fine_module() 함수를 이용해 이미 적재된 모듈인가를 검사한다.
    6. 만약 적재되지 않았다면, vmalloc() 함수를 호출해 새로운 모듈을 위한 메모리 영역을 할당한다.
    7. query_module() 함수를 통해 커널 심볼 테이블과 커널에 적재된 다른 모듈의 심볼 테이블을 구한다. (QM_MODULES, QM_INFO, QM_SYMBOL)
    8. 커널에 적재하려는 모듈의 프로그램 코드 주소를 재배치한다.
    9. 사용자 모드 주소 공간에 메모리 영역을 할당하고, 이곳에 모듈 구조체의 내용과 모듈명 그리고 재배치된 모듈 코드를 복사한다.
    10. 모듈 구조체의 init 필드의 함수 주소와 exit 필드의 함수 주소를 할당한다.
    11. 사용자 모드 주소 공간에 할당된 메모리 주소를 이용해 init_module() 함수를 호출한다.

 

  • 모듈 빌드하기
    • 커널 소스 트리에 있는 경우
      • 예시
        • Fish Master XL 2000을 위한 디바이스 드라이버(캐릭터)
        • drivers/char/ 의 Makefile에 다음을 추가
          • obj-m += fishing.o
        • drivers/char/fishing/ 안에 다음 내용을 포함하는 Makefile 작성
          • obj-m += fishing.o
        • 디바이스 드라이버 파일이 커 두 개로 나눠야 할 때
          • obj-m += fishing.o
          • fishing-objs := fishing-main.o fishing-line.o
        • 최종적인 fishing.ko가 만들어진다.

    • 커널 소스 밖에 있는 경우
      • 예시
        • Makefile을 다음과 같이 구성할 수 있다.
          • obj-m := fishing.o
          • fishing-objs := fishing-main.o fishing-line.o
        • 가장 큰 차이점은 build 과정이다.
          • make -C kernel/source/location SUBDIRS=$(PWD)
          • -C : 커널 소스 트리가 있는 위치
          • SUBDIRS : 드라이버 소스가 있는 작업 디렉토리

  • 모듈 설치
    • 컴파일된 모듈은 일반적으로 다음 위치에 설치
      • /lib/modules/version/kernel
      • 예: /lib/modules/2.6.10/kernel/drivers/char/fishing.ko
    • 일반적인 커널은 다음 빌드 명령으로 적절한 위치에 설치한다.
      • make modules_install
  • 여러개의 파일로 구성된 모듈 컴파일
    • Makefile 구성
      • start.c stop.c 코드를 가지고 모듈을 컴파일한다고 할 때

        Makefile 예시

        obj-m +=startstop.o
        startstop-objs := start.o stop.o


        KDIR := /work/ldd/linux-2.6.17.13-rebis


        all:

               make -C $(KDIR) SUBDIRS=$(PWD) modules

        clean:

               make -C $(KDIR) SUBDIRS=$(PWD) clean



  • 드라이버 매크로
    • 드라이버 정보 매크로
      • 매크로
        설명
        module_init()함수 변경 매크로
        module_exit()함수 변경 매크로
        __initinit 함수가 종료되었을 때, init 함수를 버리고, 메모리를 free로 만듦
        __initdata초기 변수에 대해 __init과 유사한 작업을 함
        __exit모듈이 커널 내로 삽입될 때 함수를 생략
        MODULE_LICENCE()GPL, GPL v2, Dual BSD/GPL, Proprietary 등
        MODULE_DESCRIPTION()모듈의 하는 일을 설명
        MODULE_AUTHOR()모듈 제작자
        MODULE_SUPPORTED_DEVICE()모듈이 지원하는 장치의 타입
    • 모듈 매개변수 매크로
      • 커널은 사용자가 매개변수를 부트시나 모듈 로드시에 선언하고 드라이버 안에서는 이 매개변수들이 전역변수처럼 보이도록 하는 프레임웍을 제공한다.

        매크
        module_param(name, type, perm)command 인자를 얻음. ex) ./insmod mynodule.ko myvariable=5
        module_param_array(name, type, nump, perm)

        인자: 변수명, 자료형, nump는 정수형 포인터, sysfs의 접근 권한.

        세번째 인자로 배열 개수를 포인터로 넘겨줌 (NULL 가능)

        module_param_string(name, string, len, perm);

        charp로 정의된 문자열을 지정할 때, len으로 길이와 함께 지정.

        길이는 sizeof(string) 형태로 사용 가능

        module_param_named(name, value, type, perm);변수의 값(value)를 내부에 포함 시키고자 할 때 사용
        MODULE_PARM_DESC()

        모듈이 가질 수 있는 문서 인자들로 사용.

        인자: 변수 이름, 변수를 묘사하는 문자열


  • 커널 메세지 출력
    • printk()
      • printf()와 유사하지만 커널의 메시지를 출력하고 관리할 수 있는 특성이 있다.
      • 메세지 기록 관리를 위한 로그 레벨의 지정
      • 출력 디바이스의 다중 지정
      • 콘솔에서 확인하거나 dmesg 명령을 이용해서 로그 파일 확인
      • 로그레벨 지정
        • 로그 레벨은 printk() 함수에 전달되는 문자열의 선두 문자에 "<1>"과 같이 숫자로 등급을 표현한다.
        • linux/kernel.h 에 정의된 선언문을 이용하는 것이 바람직하다.
        • 상수 선언문
          의미
          #define KERN_EMERG<0> 시스템이 동작하지 않는다
          #define KERN_ALERT<1> 항상 출력된다.
          #define KERN_CRIT<2> 치명적인 정보
          #define KERN_ERR<3> 오류 정보
          #define KERN_WARNING<4> 경고 정보
          #define KERN_NOTICE<5> 정상적인 정보
          #define KERN_INFO<6> 시스템 정보
          #define KERN_DEBUG<7> 디버깅 정보
        • 레벨에 대한 표시를 하지 않으면 KERN_WARNING과 같은 레벨이다.
        • 다음의 처리 결과는 모두 같다.
          • printk(KERN_WARNING "system ok\n");
          • printk("<4>" "system ok\n");
          • printk("<4> system ok\n");
          • printk("system ok\n");
        • printk() 사용시 주의점
            • 개행 문자가 있어야 출력을 시작한다.

    • 커널 메시지 관리 데몬
      • klogd: 커널에서 발생하는 메시지를 기록하고 관리한다.
      • syslogd: 커널에서 발생하는 메시지와 응용 프로그램에서 요청한 시스템 정보를 기록하고 관리한다.
      • /proc/kmsg
        • 커널 메시지가 발생할 때마다 관찰할 수 있다.
        • cat /proc/kmsg
        • dmesg

 


출처 http://blog.naver.com/PostView.nhn?blogId=yyg1368&logNo=60121733093

출처 https://wiki.kldp.org/Translations/html/The_Linux_Kernel-KLDP/tlk12.html

출처 https://www.joinc.co.kr/w/Site/Embedded/Documents/LinuxKernelModuleProg

 

 

+ Recent posts