레이블이 Android인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Android인 게시물을 표시합니다. 모든 게시물 표시

2021년 2월 16일 화요일

Flutter에서의 Layout

 내가 아는 선에서 이야기 하자면 Qt의 QHBoxLayout과 QVboxLayout에 해당하는 Flutter의 Layout에 대해서 들여다 본다. 

Qt의 Layout과 다르게 Flutter의 Layout은 정렬할 수 있는 항목에 제한이 있다. 내용을 살펴보면 굳이 구분해 가며 인지할 필요없이 당연하지 라고 말할 것이다.

* Center, Container - child라는 항목으로 단지 한개의 항목만 정렬한다. Center는 가운데 정렬로 알겠는데 Container는 아마도 Center안에 여러개를 넣기 위해서 도입된 더미 Layout이라고 추정해 본다.

* Row, Column, ListView, Stack - 앞의 둘은 각각 Qt의 QVBoxLayout, QHBoxLayout에 해당한다. 주의해야할 것은 Row, Column이라는 이름에 선입겸을 갖고 사용하면 큰일 난다. 한번 확인해 보고 사용해야 실수를 줄일 수 있다. Row와 Column이라는 지칭하는 주체가 구분되어지는 칸의 유형을 의미한다라고 나는 이해했다. 그리고 처음 보는 ListView, Stack이 있다. List는 그냥 나열되는 것이고 Stack은 화면의  Z축 방향으로 쌓는 것을 의미하는 것 같다.

아직은 추측성이 많다 하나하나 확인해 나아가겠다.

Flutter에서 StatelessWidget과 StatefulWidget with State의 차이

#  Stateless Widget

내부 변수의 변경 사항이 다른 메서드에 반영되지 못하는 특성을 가지고 있다.

아직까지의 파악으로는 UI의 디자인을 전게해나아는데 특화해서 사용하는 Widget으로 파악된다.


# StatefulWidget

통상 State Widget을 내부 객체로 포함하며, 

State Widget의 내부 변수의 변경사항에 setState() 선행 매서드를 통해 다른 구성요소에 전달되도록 하는 메카니즘을 가지고 있다.


# Stateless Widget과 StatefulWidget(State)의 예제 

MyApp이라는 Flutter Tutorial에 두번째로 출현하는 예제이다. 여기서, MyApp자체가 StatelessWidget을 상속받아서 생성되어 있다.(뭐 딱히 동적으로 변경해야할 변수들을 가지고 있지 못하다.) 그리고 StatelessWidget의 build 메서드를 override(덮어 씌우기)해서 새롭게 MaterialApp을 생성하고 있다.

(MaterialApp은 구글이 밀고 있는 Material Design을 계승하는 Widget이라고 한다.)

import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';

/*
void main() {
runApp(MyApp());
}
*/

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Startup Name Generator',
theme: ThemeData(
primaryColor: Colors.white,
),
home: RandomWords(),
);
}
}

아직 Dart언어를 모르기 때문에 하나 짚고 넘어가야할 사항이 있다. main 함수의 형태이다.

주석으로 처리되어 있는 main함수는 익히 알고 있는 C 언어 형태의 서술이고 아래의 '=>'로 엮여진 main함수는 아마도 한줄짜리로 표현한 Dart 언어 문법인 것 같다. (뭐 시작을 C언어와 유사하다고 해서 들여다 보고 있는 형편이라서 과감하게 추정해 보자면 Lambda 함수인것 같다.)

MaterialApp의 home 섹션은 RandomWords라는 Widget으로 들어차게 만든다.

class RandomWords extends StatefulWidget {
@override
RandomWordsState createState() => RandomWordsState();
}

자 여기서, RandomWords가 StatefulWidget으로 등장하고 있다. 또 override를 수행한다. 앞에서와 다르게 여긴 createState()로 구현한다. 또 과감하게 추정하자면 둘다 이름이 다를뿐 일종의 생성자 같다. 물론 부모의 기능을 초기화해야 해서 생성자 전체를 override하진 않는 것같다. 아마도 StatelessWidget과 StatefulWidget의 생성자 내부에는 여러개의 Method로 이루어져 있는 것 같다. 그 중 하나를 override하고 있다고 추정하는 바이다.

여기까지, StatelessWidget과 StatelessWidget의 구성이다. 각각  build()와 createState()라는 override를 통해서 구현하고 있다. 이 둘의 이름에서 알 수 있듯이 State가 없고 있고의 차이가 있다.

아직 설명 안된 구석이 있는데 그것은 RandomWordState라는 Widget이다.


class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _saved = Set<WordPair>();
final _biggerFont = const TextStyle(fontSize: 18.0);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Startup Name Generator'),
actions: [
IconButton(icon: Icon(Icons.list), onPressed: _pushSaved),
]
),
body: _buildSuggestions(),
);
}

Widget _buildSuggestions() {
return ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: /*1*/ (context, i) {
if (i.isOdd) return Divider(); /*2*/
final index = i ~/ 2; /*3*/
if (index >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
}
);
}

Widget _buildRow(WordPair pair) {
final alreadySaved = _saved.contains(pair);
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
trailing: Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red : null,
),
onTap: () {
setState(() {
if (alreadySaved) {
_saved.remove(pair);
} else {
_saved.add(pair);
}
});
}
);
}

void _pushSaved() {
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context) {
final tiles = _saved.map((WordPair pair) {
return ListTile(
title: Text(pair.asPascalCase, style: _biggerFont)
);
});

final divided = ListTile.divideTiles(
context: context,
tiles: tiles,
).toList();

return Scaffold(
appBar: AppBar(
title: Text('Saved Suggestions'),
),
body: ListView(children: divided),
);
},
),
);
}
}

길다. RandomWordsState는 예상했듯이 State로 부터 상속받아서 생성된다. 여기서, 의문점은 State<RandomWords>라는 구문이다. 내가 아는 C언어 선입견을 동원하면 State class를 RandomWords의 타입을 같도록 생성한 template인 것 같다. 말이 어렵다 State가 RandomWords라는 내부 항목을 가지고 작업할 것이다라고 이해하자.

아직 정체가 불확실한 final로 시작하는 변수 생성 이것도 마찬가지 그냥 넘어가자. 그냥 변수다라고만 인지하고 나중에 차차 살펴보자.

첫 번째 Method인 build()는 StatelessWidget에서 보았듯이 일종의 생성자 중 하나라고 보자. 여기서, Scaffold라는 MaterialApp과는 다른 Widget을 만들고 있다. 새로운것들이 많이 등장한다. 우선은 Scaffold라는 것은 AppBar라는 일종의 윈도우의 title bar와 내부항목에 해당하는 body라는 섹션을 가지고 있다. 

먼저 AppBar는 문자로된 title을 가지고 있고 그에 해당하는 동작을 actions에 담고 있다. IconButton을 가지고 뭘 하는 것 같다. 우선 넘어가자. 그리고 body에는 _buildSuggestions()이라는 method로 뭔가 _suggestions이라는 리스트를 만들고 보여주는 것을 수행하는 것 같다.

_buildSuggestions과 엮인 다른 Method는 생략한다. 지친다. 


# 전체 코드

import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';

/*
void main() {
runApp(MyApp());
}
*/

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Startup Name Generator',
theme: ThemeData(
primaryColor: Colors.white,
),
home: RandomWords(),
);
}
}

class RandomWords extends StatefulWidget {
@override
RandomWordsState createState() => RandomWordsState();
}

class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _saved = Set<WordPair>();
final _biggerFont = const TextStyle(fontSize: 18.0);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Startup Name Generator'),
actions: [
IconButton(icon: Icon(Icons.list), onPressed: _pushSaved),
]
),
body: _buildSuggestions(),
);
}

Widget _buildSuggestions() {
return ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: /*1*/ (context, i) {
if (i.isOdd) return Divider(); /*2*/
final index = i ~/ 2; /*3*/
if (index >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
}
);
}

Widget _buildRow(WordPair pair) {
final alreadySaved = _saved.contains(pair);
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
trailing: Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red : null,
),
onTap: () {
setState(() {
if (alreadySaved) {
_saved.remove(pair);
} else {
_saved.add(pair);
}
});
}
);
}

void _pushSaved() {
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context) {
final tiles = _saved.map((WordPair pair) {
return ListTile(
title: Text(pair.asPascalCase, style: _biggerFont)
);
});

final divided = ListTile.divideTiles(
context: context,
tiles: tiles,
).toList();

return Scaffold(
appBar: AppBar(
title: Text('Saved Suggestions'),
),
body: ListView(children: divided),
);
},
),
);
}
}


2019년 4월 26일 금요일

리눅스에서 안드로이드 카카오톡 앱 실행해보기

먼저, 리눅스 시스템에서 안드로이드 시스템을 구성해주는 anbox (Android in Box)를 설치하자.

# apt install anbox

설치는 되었지만, 실행에 뭔가 문제가 있다. 문제가 뭔지 살펴보자.

$ anbox session-manager
 [ 2019-04-25 14:41:11] [session_manager.cpp:130@operator()] Failed to start as either binder or ashmem kernel drivers are not loaded

커널 드라이버가 없어서 실행이 되지 않는 것 같다.

그래서, 설치된 패키지의 설명서를 살펴보았다.

....
This package needs Android kernel modules and rootfs image, see
/usr/share/doc/anbox/README.Debian for information.
...

따라해 보기로 했다. /usr/share/doc/anbox/README.Debian을 살펴보자.

그 내용은 요약하자면 다음과 같다.

/lib/modules/`uname -r`/kernel/drivers/android/binder_linux.ko
/lib/modules/`uname -r`/kernel/drivers/staging/android/ashmem_linux.ko
위의 두 커널 모듈이 있는지 확인하자.

그리고, https://build.anbox.io/android-images 에서 사전에 만들어진 안드로이드 이미지를 다운로드하자. 이왕이면 가장 최신의 것으로 다운로드 한다. 현 시점에선

Index of /android-images/2018/07/19

[ICO]NameLast modifiedSizeDescription

[PARENTDIR]Parent Directory-
[   ]android_amd64.img2018-07-20 01:46 311M
[   ]android_amd64.img.sha256sum2018-07-20 02:11 84


이 최신이다.

이제 부터는 좀 불친절하게 되어 있다. 말도 많고 탈도 많은 systemd와 관련된 문제이다.
다자고짜 anbox-container-manager.service 를 실행하라고 한다.
찾아보니 다음과 같이 하면 될 것 같다. 우선 해보기로 했다.

# systemctl start anbox-container-manager.service

얼추 된 것 같다. 한번 실행해 보자. .... 안된다. 뭔가 빠진 것 같다.
 다시 한번 더 실행해보자

$ anbox session-manager
 [ 2019-04-25 15:01:08] [session_manager.cpp:130@operator()] Failed to start as either binder or ashmem kernel drivers are not loaded

여전하다. 커널 모듈 로딩 목록에 강제적으로 넣어 주자. 아래와 같이...

@/etc/modules
# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
ashmem_linux
binder_linux


그리고 리부팅...

다시 실행.

$ anbox session-manager
[ 2019-04-25 15:07:35] [daemon.cpp:61@Run] Failed to connect to socket /run/anbox-container.socket: No such file or directory


아 돌겠네.

# systemctl start anbox-container-manger.service

또 실행.

$ anbox session-manager
[ 2019-04-25 15:09:28] [daemon.cpp:61@Run] Failed to connect to socket /run/anbox-container.socket: No such file or directory


서비스에 문제가 있나보다. 휴우...




# systemctl status anbox-container-manager.service
● anbox-container-manager.service - Anbox Container Manager
   Loaded: loaded (/lib/systemd/system/anbox-container-manager.service; enabled;
   Active: inactive (dead)
Condition: start condition failed at Fri 2019-04-26 00:06:55 KST; 4min 6s ago
           └─ ConditionPathExists=/var/lib/anbox/android.img was not met
     Docs: man:anbox(1)

 4월 26 00:05:22 T480s systemd[1]: Condition check resulted in Anbox Container M
 4월 26 00:06:55 T480s systemd[1]: Condition check resulted in Anbox Container M
lines 1-9/9 (END)




안드로이드 이미지를 못찾는다. 다운로드한 이미지 /var/lib/anbox 에 복사.

$ su
# anbox container-manager
[ 2019-04-25 15:17:48] [container_manager.cpp:71@operator()] You are running the container manager manually which is most likely not
[ 2019-04-25 15:17:48] [container_manager.cpp:72@operator()] what you want. The container manager is normally started by systemd or
[ 2019-04-25 15:17:48] [container_manager.cpp:73@operator()] another init system. If you still want to run the container-manager
[ 2019-04-25 15:17:48] [container_manager.cpp:74@operator()] you can get rid of this warning by starting with the --daemon option.
[ 2019-04-25 15:17:48] [container_manager.cpp:75@operator()]
[ 2019-04-25 15:17:48] [container_manager.cpp:119@operator()] boost::filesystem::create_directories: Invalid argument
# exit

$ anbox session-manager
[ 2019-04-25 15:18:23] [daemon.cpp:61@Run] Failed to connect to socket /run/anbox-container.socket: No such file or directory


장시간 복기 끝에 문제점을 찾은 것 같다.

/var/lib/anbox 에 이름을 android_amd64.img에서 android.img로 안바꾼 것이 탈이 된 것 같다.

anbox-container-manager.service 를 멈추고 다시 시작하자.

# systemctl stop anbox-container-manager.service
# systemctl start anbox-container-manager.service
# systemctl status anbox-container-manager.service
● anbox-container-manager.service - Anbox Container Manager
   Loaded: loaded (/lib/systemd/system/anbox-container-manager.service; enabled;
   Active: active (running) since Fri 2019-04-26 00:23:27 KST; 5s ago
     Docs: man:anbox(1)
  Process: 27421 ExecStartPre=/sbin/modprobe ashmem_linux (code=exited, status=0
  Process: 27422 ExecStartPre=/sbin/modprobe binder_linux (code=exited, status=0
  Process: 27423 ExecStartPre=/usr/share/anbox/anbox-bridge.sh start (code=exite
 Main PID: 27499 (anbox)
    Tasks: 9 (limit: 4915)
   Memory: 4.8M
   CGroup: /system.slice/anbox-container-manager.service
           └─27499 /usr/bin/anbox container-manager --daemon --privileged --data

 4월 26 00:23:27 T480s systemd[1]: Starting Anbox Container Manager...
 4월 26 00:23:27 T480s systemd[1]: Started Anbox Container Manager.
 

뭔가 느낌이 좋다. 한번 더 실행해보자.




된다.

2013년 6월 16일 일요일

안드로이드 개발 환경 설정

시스템은 데비안 리눅스 testing 배포판이고 기존에 eclipse 3.8 버전이 설치 되어 있다.

먼저, 새로운 android 번들 시스템이 있어서 이전 처럼 eclipse 플러그인과 android SDK를

따로 따로 설치해서 설정해야 하는 과정이 사라 진 것 같다.

우선 둘다 써보고 나중에 편한 것으로 선택해야 겠다.

번들 개발 환경의 설정은 쉬우니깐 eclipse와 android sdk, 그리고 eclipse plugin을 설치하는 방식을 기록해야 겠다.

우선 android sdk를 다운로드 하고 설치하자.

http://developer.android.com/sdk/index.html#ExistingIDE 에 가서


Download the SDK Tools for Linux를 클릭하여 android-sdk_r22.0.1-linux.tgz를 다운 로드하고,

적절한 위치에 압축 푼다. 나의 경우 /home/{username}/ 밑에 압축해제하였다.

그리고 /home/{username}/android-sdk-linux/tools 밑의 android라는 실행 파일을 실행시킨다.


최신 버전의 API와 기본적인  Tools을 선택하고 라이센스에 동의함을 선택한다.


선택한 패키지의 설치를 수행한다. 


설치가 마무리 되면 다음은 eclipse 설정이 필요하다. 

eclipse의 help->install new software를 선택한다.


Add 버튼을 클릭한다.



Android Development Tool을 가지고 있는 Eclipse plugin 주소를 추가한다.


해당 패키지 중 Developer Tools을 선택하고 Next> 버튼을 실행한다.



혹시 다른 패키지의 의존성 문제로 설치가 안되는 경우가 간혹 있다.

나의 경우가 그러한데 해결책은 eclipse를 지우고 다시 설치하는 방법이 가장 쉬운 방법인것 같다.

설치할 패키지는 다음과 같다. 아마도 eclipse-cdt만 설치하거나 한가지가 누락 되거나 한것 같다.

# apt-get install eclipse eclipse-cdt eclipse-jdt

위와 같은 과정을 거치고 eclipse를 다시 실행하면  android-sdk의 경로가 잘못되었다는 메시지와

함께 설정 다이어로그를 열 수 있게 해준다. 


자 기본적인 것들은 마무리 된 것 같다.


2013년 2월 19일 화요일

안드로이드 스마트폰의 micro USB 포트의 모드 변환

아는 사람의 부탁으로 스마트폰의 micro USB 포트가 USB 뿐만 아니라 오디오, 해드셋, 충전 기타 등등의 많은 기능을 수행한다는 것을 알게되었다. 하지만, 아이폰에서 그 많은 기능을 하기위해서 많은 핀들이 존재하는데 다른 스마트폰에서 5핀 밖에 안되는 micro USB 포트로 수행한다는 것은 뭔가 추가적인 기능을 하는 부분이 존재하리라 짐작된다.
검색을 해보니 삼성 스마트폰에만 해당하는 내용일 수 도 있겠지만 FSA9480라는 Fairchild사의 USB 스위치라는 것이 이러한 기능을 수행하도록 되어 있다고 한다. 기본적인 원리는 USB 포트의 D+와 D- 사이에 걸리는 저항값을 통해서 USB OTG로 동작할 것인지, 해드셋으로 동작할 것이지 등등의 모드 전환을 수행한다고 되어 있다. 


USB ID resistor values


Mode Name
Resisor value
Internal mode value
Description

RID_USB_OTG_MODE
0 (GND)
0 0 0 0 0
USB OTG Mode

RID_AUD_SEND_END_BTN
2 K
0 0 0 0 1
Audio Send_End Button

RID_AUD_REMOTE_S1_BTN
2.604 K
0 0 0 1 0
Audio Remote S1 Button

RID_AUD_REMOTE_S2_BTN
3.208 K
0 0 0 1 1
Audio Remote S2 Button

RID_AUD_REMOTE_S3_BTN
4.014 K
0 0 1 0 0
Audio Remote S3 Button

RID_AUD_REMOTE_S4_BTN
4.82 K
0 0 1 0 1
Audio Remote S4 Button

RID_AUD_REMOTE_S5_BTN
6.03 K
0 0 1 1 0
Audio Remote S5 Button

RID_AUD_REMOTE_S6_BTN
8.03 K
0 0 1 1 1
Audio Remote S6 Button

RID_AUD_REMOTE_S7_BTN
10.03 K
0 1 0 0 0
Audio Remote S7 Button

RID_AUD_REMOTE_S8_BTN
12.03 K
0 1 0 0 1
Audio Remote S8 Button

RID_AUD_REMOTE_S9_BTN
14.46 K
0 1 0 1 0
Audio Remote S9 Button

RID_AUD_REMOTE_S10_BTN
17.26 K
0 1 0 1 1
Audio Remote S10 Button

RID_AUD_REMOTE_S11_BTN
20.5 K
0 1 1 0 0
Audio Remote S11 Button

RID_AUD_REMOTE_S12_BTN
24.07 K
0 1 1 0 1
Audio Remote S12 Button

RID_RESERVED_1
28.7 K
0 1 1 1 0
Reserved Accessory #1

RID_RESERVED_2
34 K
0 1 1 1 1
Reserved Accessory #2

RID_RESERVED_3
40.2 K
1 0 0 0 0
Reserved Accessory #3

RID_RESERVED_4
49.9 K
1 0 0 0 1
Reserved Accessory #4

RID_RESERVED_5
64.9 K
1 0 0 1 0
Reserved Accessory #5

RID_AUD_DEV_TY_2
80.07 K
1 0 0 1 1
Audio Device Type 2

RID_PHONE_PWD_DEV
102 K
1 0 1 0 0
Phone Powered Device

RID_TTY_CONVERTER
121 K
1 0 1 0 1
TTY Converter

RID_UART_CABLE
150 K
1 0 1 1 0
UART Cable

RID_CEA936A_TY_1
200 K
1 0 1 1 1
CEA936A Type-1 Charger 1

RID_FM_BOOT_OFF_USB
255 K
1 1 0 0 0
Factory Mode Boot OFF-USB

RID_FM_BOOT_ON_USB
301 K
1 1 0 0 1
Factory Mode Boot ON-USB

RID_AUD_VDO_CABLE
365 K
1 1 0 1 0
Audio/Video Cable

RID_CEA936A_TY_2
442 K
1 1 0 1 1
CEA936A Type-2 Charger 1

RID_FM_BOOT_OFF_UART
523 K
1 1 1 0 0
Factory Mode Boot OFF-UART

RID_FM_BOOT_ON_UART
619 K
1 1 1 0 1
Factory Mode Boot ON-UART

RID_AUD_DEV_TY_1_REMOTE
1000.07 K
1 1 1 1 0
Audio Device Type 1 with Remote 2

RID_AUD_DEV_TY_1_SEND
1002 K
Audio Device Type 1 / Only Send-End 2

RID_USB_MODE
Open
1 1 1 1 1
USB Mode, Dedicated Charger or Accessory Detach



출처 : http://www.jetdroid.org/moin/SamsungUARTdebugging

이와 같은 기능을 이용해서 삼성 스마트폰 제조 과정에서 스마트폰의 정상동작을 점검하는 디버그 장비로 활용하는 것같다. 이름도 얄딱구리한 Samsung Anyway JIG라는 것이 바로 그것인 것 같다.



출처 : http://forum.xda-developers.com/showthread.php?t=1629359


비슷한 기능의 것들이 다른 스마트폰에도 있을 것이다. 우선은 위의 기능을 이용해서 micro USB 포트를 다른 모드로 동작하도록 해 보아야 겠다. 디버깅 포트로도 해보고 오디오 기능으로도 해보고 말이다. 그런데 삼성 스마트 폰이 없다. ㅋㅋㅋ

2012년 3월 2일 금요일

Preparing android development environment in debian(wheezy)

* Install eclipse-cdt
# apt-get install eclipse-cdt

* According to documentation for android development, at first it recommended to "install android sdk starter kit" but it is not useless because the same packages are installed under "~/android-sdks" through installation of ADT plugin for eclipse

* Add address of ADT plugin for eclipse
1. Execute "eclipse"
2. Go to "Install New Software" under "Help"
3. Click "Add" and insert "http://dl-ssl.google.com/android/eclipse"
4. Install 4 plugins for android
 If there is lack of few plugins in Eclipse Indigo such that "requires 'org.eclipse.wst.sse.core 0.0.0' but it could not be found"
then add address of release plugins for "eclipse helios" and check "Contact all updates sites during install to find required software"
5. Just wait for so long times.


2011년 6월 16일 목요일

What is Goldfish in Android ?

안드로이드 SDK는 특정 (ARM) SoC(System on Chip)만을 고려해서 작성되지 않았다. 대신에 Goldfish라고 불리는 실질적인 ARM Soc를 형성할 수 있는 QEMU를 사용한다. (QEMU는 PC에서 사용할 수 있는 processor 에뮬레이터이다. 리눅스 패키지에서 쉽게 찾아 볼 수있다.) 실질적인 ARM Soc는 여러분의 (x86) 윈도우즈, MacOs 혹은 리눅스 호스트를 지원하는 Goldfish 플렛폼을 포함한 ARM 리눅스 커널(현재 2.6.23, m5-rc14버전)로 부팅한다.
이 실질적인 ARM SoC는 다음과 같은 것을 지원한다.
* ARM926ej-S CPU
* Thumb모드 지원
* MMC
* RTC
* Keyboard
* USB Gadget
* Framebuffer
* TTY driver
* NAND
* EABI를 지원하는 ARMv5TEJ명령어로 컴파일된 소프트웨어
* no TLS yet
출처 : http://elinux.org/Android_on_OMAP

2010년 9월 26일 일요일

Working note to replace booting logo of android

At init.c under system/core/init

* remove text logo

#if BOOTLOGO
if( logo_565rle_image(INIT_IMAGE_FILE) ) {
#endif
+#if 0
fd = open("/dev/tty0", O_WRONLY);
if (fd >= 0) {
#if 1 /* 0xlab flavor */
#include "text_0xlab.h"
#else
const char *msg;
msg = "\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n" // consol is 40 cols x 30 lines
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
" ANDROID ";
#endif
write(fd, msg, strlen(msg));
close(fd);
}
+#endif

* create new boot logo image with "rle" extension; means run length encoding
create new "bmp" image file with 24bits true color
convert "bmp" file using "rgb2565" under "out/host/linux-x86/bin"; #./rgb2565 -rle < "bmp" > initlogo.rle
replace initlogo.rle under "vender/0xlab/devkit8000/initlogo.rle"

2010년 7월 15일 목요일

CameraHardwareInterface between Cupcake and Eclair

변화가 많다. 우선 기능별로 각각의 함수 내에서 동작하던 Callback 함수들을 하나로 통합하여 관리하고 카메라 동작시 문제가 많이 발생하였는지 mutex로 안정성을 공고히 하였다. proyo에서도 많이 변화가 예상된다. 나오거든 한번 봐야 겠다.

Customize Android.mk for libcameraservice to use a real camera hardware not fake camera

  • Android.mk under frameworks/base/camera/libcameraservice/
LOCAL_PATH:= $(call my-dir)

#
# Set USE_CAMERA_STUB for non-emulator and non-simulator builds, if you want
# the camera service to use the fake camera. For emulator or simulator builds,
# we always use the fake camera.

ifeq ($(USE_CAMERA_STUB),)
USE_CAMERA_STUB:=false
ifneq ($(filter sooner generic sim,$(TARGET_DEVICE)),)
USE_CAMERA_STUB:=true
endif #libcamerastub
endif

ifeq ($(USE_CAMERA_STUB),true)
#
# libcamerastub
#

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
CameraHardwareStub.cpp \
FakeCamera.cpp

LOCAL_MODULE:= libcamerastub

ifeq ($(TARGET_SIMULATOR),true)
LOCAL_CFLAGS += -DSINGLE_PROCESS
endif

LOCAL_SHARED_LIBRARIES:= libui

include $(BUILD_STATIC_LIBRARY)
else
#
# libcamera
#

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
CameraHardware.cpp \
V4L2Camera.cpp

LOCAL_MODULE:= libcamera

LOCAL_SHARED_LIBRARIES:= \
libui \
libutils \
libcutils

LOCAL_STATIC_LIBRARIES:= \
libjpeg

LOCAL_C_INCLUDES += \
external/jpeg

include $(BUILD_STATIC_LIBRARY)
endif # USE_CAMERA_STUB

#
# libcameraservice
#

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
CameraService.cpp

LOCAL_SHARED_LIBRARIES:= \
libui \
libutils \
libbinder \
libcutils \
libmedia

LOCAL_MODULE:= libcameraservice

LOCAL_CFLAGS += -DLOG_TAG=\"CameraService\"

ifeq ($(TARGET_SIMULATOR),true)
LOCAL_CFLAGS += -DSINGLE_PROCESS
endif

ifeq ($(USE_CAMERA_STUB), true)
LOCAL_STATIC_LIBRARIES += libcamerastub
LOCAL_CFLAGS += -include CameraHardwareStub.h
else
LOCAL_STATIC_LIBRARIES += libcamera
LOCAL_CFLAGS += -include CameraHardware.h
endif

include $(BUILD_SHARED_LIBRARY)

  • init.rc under vendor/0xlab/devkit8000
# add these lines
chown root camera /dev/video0
chmod 0660 /dev/video0

The CameraHardwareInterface class has some changes in eclair version compare to donut version; Callback functions attached to individual function are removed but are handled by predefined messages.

2010년 7월 11일 일요일

Add camera library to Android

현재 구동되고 있는 안드로이드 카메라 인터페이스는 fake camera와 camera stub이라는 부분이 연결되어 구동되고 있는 자세히는 모르겠으나 아마도 NULL camera로 되어 있는 거 같다. 이 부분을 지금구동되고 있는 하드웨어 /dev/video0에 연결하는 작업이 필요하다. 그래서 찾아본 결과 다음과 같은 android.mk파일과 v4l2_camera_v2.patch라는 파일을 보고 수정을 가해주어야한다는 힌트를 얻었다. 우선은 바로 적용이 안될 것이라 예상되기 때문에 한줄 한줄 읽어 가면서 적용해 나아가야 할것같다.
(참조한 문서 : http://www.mail-archive.com/android-porting@googlegroups.com/msg06060.html, 와 http://code.google.com/p/android-m912/downloads/list )

그레서 우선 patch명령으로 수정되는 사항을 살펴보기 위해서 patch명령어가 수행하는 동작들을 알아봐야한다.

2010년 6월 14일 월요일

About omap34xxcam_init in omap34xxcam.c

{
struct omap34xxcam_device *cam;
int i;

cam = kzalloc(sizeof(*cam), GFP_KERNEL);
if (!cam)
return -ENOMEM;

omap34xxcam = cam;

for (i = 0; i <>
struct omap34xxcam_videodev *vdev = &cam->vdevs[i];
struct v4l2_int_device *m = &vdev->master;

m->module = THIS_MODULE;
strcpy(m->name, CAM_NAME, sizeof(m->name));
m->type = v4l2_int_type_master;
m->u.master = &omap34xxcam_master;
m->priv = vdev;

mutex_init(&vdev->mutex);
vdev->index = i;
vdev->cam = cam;
vdev->vdev_sensor =
vdev->vdev_lens =
vdev->vdev_flash = v4l2_int_deice_dummy();
#ifdef OMAP34XXCAM_POWEROFF_DELAY
setup_timer(&vdev->poweroff_timer, omap34xxcam_slave_power_timer, (unsigned long)vdev);
INIT_WORK(vdev->poweroff_work, omap34xxcam_slave_power_work);
#endif /* OMAP34XXCAM_POWEROFF_DELAY */
if (v4l2_int_device_register(m))
goto err;
}
return 0;

err;
omap34xxcam_exit();
return -ENODEV;
}

To understand this code, firstly see "struct omap34xxcam_device"
In drivers/media/video/omap34xxcam.h
...
#define OMAP34XXCAM_VIDEODEVS 4
...

struct omap34xxcam_device {
struct mutex mutex; /* serialises access to this structure */

/*** interfaces and device ***/
struct omap34xxcam_videodev vdevs[OMAP34XXCAM_VIDEODEVS];

/*** camera module clocks ***/
struct clk *fck;
struct clk *ick;
bool sensor_if_enabled;
};

It contains OMAP34XXCAM_VIDEODEVS ( 4) video devices.

They must be different from each other. But 4 vdev are copied as same contents.

for (i = 0; i <>
struct omap34xxcam_videodev *vdev = &cam->vdevs[i];
struct v4l2_int_device *m = &vdev->master;

m->module = THIS_MODULE;
strcpy(m->name, CAM_NAME, sizeof(m->name));
m->type = v4l2_int_type_master;
m->u.master = &omap34xxcam_master;
m->priv = vdev;

mutex_init(&vdev->mutex);
vdev->index = i;
vdev->cam = cam;
vdev->vdev_sensor =
vdev->vdev_lens =
vdev->vdev_flash = v4l2_int_deice_dummy();
#ifdef OMAP34XXCAM_POWEROFF_DELAY
setup_timer(&vdev->poweroff_timer, omap34xxcam_slave_power_timer, (unsigned long)vdev);
INIT_WORK(vdev->poweroff_work, omap34xxcam_slave_power_work);
#endif /* OMAP34XXCAM_POWEROFF_DELAY */
if (v4l2_int_device_register(m))
goto err;
}

So when i = 1, the linux system has oops.

There are two ways to solve this problem.

One is to change OMAP34XXCAM_VIDEODEVS to '1'

Another is to rewrite "omap34xxcam_init" routine to have different contents.

2010년 6월 7일 월요일

하드웨어 의존적인 OV3640 코드 0xdroid의 커널의 board-devkit8000에 추가하기

Board-devkit8k.c
먼저 앞서 ov3640, Kconfig, 그리고 Makefile을 수정하여 Oxlab에서 배포하는 커널에 ov3640을 컴파일 할 수 있는 환경을 만들어 두었다. 나머지 부분은 실제 보드와 연동하여 동작 할 수 있는 환경을 만들어 주어야한다. 아래의 것들이 그에 해당하는 것인데, 이는 모두 ov3640을 지원하는 rowboat 안드로이드 배포판의 리눅스 커널에서 따왔다. Grep -r ov3640 * 으로 검색하여 보면 ov3640을 지원하는 보드는 딱 두가지 인데 그중 하나인 board-3430sdp의 코드를 보고 해당사항들을 board-devkit8000.c에 추가하고 여기에 기록으로 남긴다.

//#include
#ifdef CONFIG_VIDEO_OMAP3
#include
#include <../drivers/media/video/omap34xxcam.h>
#include <../drivers/media/video/isp/ispreg.h>
//#define REG_SDP3430_FPGA_GPIO_2 (0x50)
#define REG_DEVKIT8000_FPGA_GPIO_2 (0x50)
#define FPGA_SPR_GPIO1_3v3 (0x1 << 14)
#define FPGA_GPIO6_DIR_CTRL (0x1 << 6)
static void __iomem *fpga_map_addr;

// TODO:
#if 0
#if defined(CONFIG_VIDEO_MT9P012) || defined(CONFIG_VIDEO_MT9P012_MODULE)
/* Sensor specific GPIO signals */
#define MT9P012_RESET_GPIO 98
#define MT9P012_STANDBY_GPIO 58

#define MT9P012_USE_XCLKA 0
#define MT9P012_USE_XCLKB 1

#define VAUX_2_8_V 0x09
#define VAUX_DEV_GRP_P1 0x20
#define VAUX_DEV_GRP_NONE 0x00

#include
static enum v4l2_power mt9p012_previous_power = V4L2_POWER_OFF;
#endif
#endif

#if defined(CONFIG_VIDEO_OV3640) || defined(CONFIG_VIDEO_OV3640_MODULE)
#include <../drivers/media/video/ov3640.h>
#include <../drivers/media/video/isp/ispcsi2.h>
static struct omap34xxcam_hw_config *hwc;
#define OV3640_CSI2_CLOCK_POLARITY 0 /* +/- pin order */
#define OV3640_CSI2_DATA0_POLARITY 0 /* +/- pin order */
#define OV3640_CSI2_DATA1_POLARITY 0 /* +/- pin order */
#define OV3640_CSI2_CLOCK_LANE 1 /* Clock lane position: 1 */
#define OV3640_CSI2_DATA0_LANE 2 /* Data0 lane position: 2 */
#define OV3640_CSI2_DATA1_LANE 3 /* Data1 lane position: 3 */
#define OV3640_CSI2_PHY_THS_TERM 4
#define OV3640_CSI2_PHY_THS_SETTLE 14
#define OV3640_CSI2_PHY_TCLK_TERM 0
#define OV3640_CSI2_PHY_TCLK_MISS 1
#define OV3640_CSI2_PHY_TCLK_SETTLE 14
#endif
#endif

위의 코드는 v4l2(video4linux 2), Omap ISP코어와 연동하기위한 해더파일과 ov3640을 구동하기위해서 필요한 하드웨어 의존적이지 않은 코드의 해더파일을 추가하고 덧붙여 필요한 사전 정의 문구들이다. 한편 원본을 훼손하지 않기 위해 CONFIG_VIDEO_MT9P012에 관한 부분이 불가피하게 추가 되었지만, 시간 관계상 CONFIG_VIDEO_MT9P012부분은 막아 놓았다.

#ifdef CONFIG_VIDEO_OMAP3
//#define DEBUG_BASE 0x08000000
//#define REG_SDP3430_FPGA_GPIO_2 (0x50)
//#define FPGA_SPR_GPIO1_3v3 (0x1 << 14)
//#define FPGA_GPIO6_DIR_CTRL (0x1 << 6)

static void __iomem *fpga_map_addr;

static void enable_fpga_vio_1v8(u8 enable)
{
u16 reg_val;

fpga_map_addr = ioremap(DEBUG_BASE, 4096);
//reg_val = readw(fpga_map_addr + REG_SDP3430_FPGA_GPIO_2);
reg_val = readw(fpga_map_addr + REG_DEVKIT8000_FPGA_GPIO_2);

/* Ensure that the SPR_GPIO1_3v3 is 0 - powered off.. 1 is on */
if (reg_val & FPGA_SPR_GPIO1_3v3) {
reg_val |= FPGA_SPR_GPIO1_3v3;
reg_val |= FPGA_GPIO6_DIR_CTRL; /* output mode */
//writew(reg_val, fpga_map_addr + REG_SDP3430_FPGA_GPIO_2);
writew(reg_val, fpga_map_addr + REG_DEVKIT8000_FPGA_GPIO_2);
/* give a few milli sec to settle down
* Let the sensor also settle down.. if required..
*/
if (enable)
mdelay(10);
}

if (enable) {
reg_val |= FPGA_SPR_GPIO1_3v3 | FPGA_GPIO6_DIR_CTRL;
//writew(reg_val, fpga_map_addr + REG_SDP3430_FPGA_GPIO_2);
writew(reg_val, fpga_map_addr + REG_DEVKIT8000_FPGA_GPIO_2);
}
/* Vrise time for the voltage - should be less than 1 ms */
mdelay(1);
}
#endif

이 부분은 아마도 카메라와 주고 받는 I/O의 전원을 1.8V로 설정하는 부분인것 같다. 자세한 ioremap의 기능은 모르겠으나 REG_DEVKIT8000_FPGA_GPIO_2의 값을 읽어 와서 3.3V로 되어 있다면 설정을 지우는 작업과 약간의 딜레이를 주는 코드로 이해하도록 하겠다. 자세히 알고 싶다면 ioremap의 기능과 REG_DEVKIT8000_FPGA_GPIO_2의 레지스터를 확인해 보도록 하자.


#if defined(CONFIG_VIDEO_OV3640) || defined(CONFIG_VIDEO_OV3640_MODULE)

static struct omap34xxcam_sensor_config ov3640_hwc = {
.sensor_isp = 0,
#if defined(CONFIG_VIDEO_OV3640_CSI2)
.xclk = OMAP34XXCAM_XCLK_B,
#else
.xclk = OMAP34XXCAM_XCLK_A,
#endif
.capture_mem = PAGE_ALIGN(2048 * 1536 * 2) * 2,
.ival_default = { .numerator = 1, .demoninator = 30,},
};

static struct isp_interface_config ov3640_if_config = {
.ccdc_par_ser = ISP_CSIA,
.dataline_shift = 0x0,
.hsvs_syncdetect = ISPCTRL_SYNC_DETECT_VSRISE,
.strobe = 0x0,
.prestrobe = 0x0,
.shutter = 0x0,
// .prev_sph = 2,
// .prev_slv = 1,
.wenlog = ISPCCDC_CFG_WENLOG_AND,
.wait_hs_vs = 2,
.u.csi.crc = 0x0,
.u.csi.mode = 0x0,
.u.csi.edge = 0x0,
.u.csi.signalling = 0x0,
.u.csi.strobe_clock_inv = 0x0,
.u.csi.vs_edge = 0x0,
.u.csi.channel = 0x1,
.u.csi.vpclk = 0x1,
.u.csi.data_start = 0x0,
.u.csi.data_size = 0x0,
.u.csi.format = V4L2_PIX_FMT_SGRBG10,
};

static int ov3640_sensor_set_prv_data(void *priv)
{
hwc = priv;
hwc->u.sensor.xclk = ov3640_hwc.xclk;
hwc->u.sensor.sensor_isp = ov3640_hwc.sensor_isp;
hwc->u.sensor.capture_mem = ov3640_hwc.capture_mem;
hwc->dev_index = 1;
hwc->dev_minor = 4;
hwc->dev_type = OMAP34XXCAM_SLAVE_SENSOR;
return 0;
}

static int ov3640_sensor_power_set(enum v4l2_power power)
{
struct isp_csi2_lanes_cfg lanecfg;
struct isp_csi2_phy_cfg phyconfig;
static enum v4l2_power previous_power = V4L2_POWER_OFF;
switch (power) {
case V4L2_POWER_ON:
if (previous_power == V4L2_POWER_OFF)
isp_csi2_reset();

lanecfg.clk.pol = OV3640_CSI2_CLOCK_POLARITY;
lanecfg.clk.pos = OV3640_CSI2_CLOCK_LANE;
lanecfg.data[0].pol = OV3640_CSI2_DATA0_POLARITY;
lanecfg.data[0].pos = OV3640_CSI2_DATA0_LANE;
lanecfg.data[1].pol = OV3640_CSI2_DATA1_POLARITY;
lanecfg.data[1].pos = OV3640_CSI2_DATA1_LANE;
lanecfg.data[2].pol = 0;
lanecfg.data[2].pos = 0;
lanecfg.data[3].pol = 0;
lanecfg.data[3].pos = 0;
isp_csi2_complexio_lanes_config(&lanecfg);
isp_csi2_complexio_lanes_update(true);

phyconfig.ths_term = OV3640_CSI2_PHY_THS_TERM;
phyconfig.ths_settle = OV3640_CSI2_PHY_THS_SETTLE;
phyconfig.tclk_term = OV3640_CSI2_PHY_TCLK_TERM;
phyconfig.tclk_miss = OV3640_CSI2_PHY_TCLK_MISS;
phyconfig.tclk_settle = OV3640_CSI2_PHY_TCLK_SETTLE;
isp_csi2_phy_config(&phyconfig);
isp_csi2_phy_update(true);

isp_configure_interface(&ov3640_if_config);

if (previous_power == V4L2_POWER_OFF) {

#ifdef CONFIG_TWL4030_CORE
/* turn on analog power */
#if defined(CONFIG_VIDEO_OV3640_CSI2)
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
VAUX_1_8_V, TWL4030_VAUX4_DEDICATED);
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
VAUX_DEV_GRP_P1, TWL4030_VAUX4_DEV_GRP);
#else
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
VAUX_2_8_V, TWL4030_VAUX2_DEDICATED);
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
VAUX_DEV_GRP_P1, TWL4030_VAUX2_DEV_GRP);
#endif
udelay(100);
#else
#error "no power companion board defined!"
#endif
/* Request and configure gpio pins */
if (omap_request_gpio(OV3640_RESET_GPIO) != 0) {
printk(KERN_ERR "Could not request GPIO %d",
OV3640_RESET_GPIO);
return -EIO;
}
if (omap_request_gpio(OV3640_STANDBY_GPIO) != 0) {
printk(KERN_ERR "Could not request GPIO %d",
OV3640_STANDBY_GPIO);
return -EIO;
}
/* set to output mode */
gpio_direction_output(OV3640_RESET_GPIO, true);
gpio_direction_output(OV3640_STANDBY_GPIO, true);

/* Turn ON Omnivision sensor */
gpio_set_value(OV3640_RESET_GPIO, 1);
gpio_set_value(OV3640_STANDBY_GPIO, 0);
udelay(100);

/* RESET Omnivision sensor */
gpio_set_value(OV3640_RESET_GPIO, 0);
udelay(100);
gpio_set_value(OV3640_RESET_GPIO, 1);

/* Wait 10 ms */
mdelay(10);
enable_fpga_vio_1v8(1);
udelay(100);
}
break;
case V4L2_POWER_OFF:
/* Power Down Sequence */
isp_csi2_complexio_power(ISP_CSI2_POWER_OFF);
#ifdef CONFIG_TWL4030_CORE
#if defined(CONFIG_VIDEO_OV3640_CSI2)
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
VAUX_DEV_GRP_NONE, TWL4030_VAUX4_DEV_GRP);
#else
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
VAUX_DEV_GRP_NONE, TWL4030_VAUX2_DEV_GRP);
#endif
#else
#error "no power companion board defined!"
#endif
enable_fpga_vio_1v8(0);
omap_free_gpio(OV3640_RESET_GPIO);
iounmap(fpga_map_addr);
omap_free_gpio(OV3640_STANDBY_GPIO);
break;
case V4L2_POWER_STANDBY:
break;
}
previous_power = power;
return 0;
}

//static struct ov3640_platform_data sdp3430_ov3640_platform_data = {
static struct ov3640_platform_data devkit8000_ov3640_platform_data = {
.power_set = ov3640_sensor_power_set,
.priv_data_set = ov3640_sensor_set_prv_data,
.default_regs = ov3640_common[0],
};

#endif
이 부분은 ov3640의 기초적인 부분은 말하자면 전원 끄고 리셋하고 i2c 데이터 날리는 부분이 되는거 같다. 세세한 사항은 남겨두고 중유한 부분은 static struct ov3640_platform_data devkit8000_ov3640_platform_data 이 구조체가 되겠다. 이 부분이 i2c 드라이버에 등록되어 실제 (ov3640 드라이버 (driver/media/video/ov3640.c)가 구동 될때 i2c 버스에 달랑 등록하는 부분만 있다. 그럼 나머지 부분인 초기화 하고 제어하는 하드웨어 의존적인 부부은 이곳에서 한다.) ov3640 드라이버가 i2c 드라이버에 추가될때 검색하여 실행할 것들을 모아 놓은 것이다. 그리고 아래 부분은 i2c의 2번째 채널에 devkit8000_ov3640_platform_data을 등록하는 부분이다.

(추가 수정사항 :
static struct omap34xxcam_sensor_config ov3640_hwc의 값 초기화에 멤버 변수 ival_default에 대한 초기화 추가
omap34xxcam.c의 버전 차이(자세히 이야기 하자면 변종)로 인한 추가 멤버가 생겼다. Struct v4l2_fract ival_default는 프레임 레이트(fps:frame per second)이 정의되어 있지 않을때 사용해야할 값을 넣어 두고는 곳이 되는것 같다. 그 구성과 사용하는 값은 다음과 같다.
Struct v4l2_fract {
__u32 numerator;
__u32 denominator;
};
.ival_fract = { .numerator = 1, .denominator = 30, },
즉, 30fps의 활동사진을 보여주도록 설정한다.

CONFIG_VIDEO_OMAP3_CAM을 CONFIG_VIDEO_OMAP3로 수정
rowboar의 커널에서 CONFIG_VIDEO_OMAP3_CAM으로 정의되어 사용하는 것이 0xdroid의 경우 CONFIG_VIDEO_OMAP3로 되어 있다.

Struct isp_interface_config ov3640_if_config의 prev_sph, 와 prev_slv의 초기화 제거
drivers/media/video/isp/isp.c의 버전 차이로 인해 prev_sph와 prev_slv가 제거되고 isppreview_set_skip(config->prev_sph, config->prev_slv); 가 사라져 버렸다. 어떤것이 상위 버전인지는 알수가 없다.
omap_request_gpio가 없다는 에러
gpio드라이버의 버전 호환 문제로 인한 에러로 여겨짐

omap_request_gpio(OV3640_RESET_GPIO) => gpio_request(0V3640_RESET_GPIO, "ov3640 reset gpio")

omap_request_gpio(OV3640_STANDBY_GPIO) => gpio_request(OV3640_STANDBY_GPIO, "ov3640 standby gpio")

omap_free_gpio => gpio_free

로 변경하자.

)

//static struct i2c_board_info __initdata sdp3430_i2c_boardinfo_2[] = {
static struct i2c_board_info __initdata devkit8000_i2c_boardinfo_2[] = {
// TODO:
#if 0
#if defined(CONFIG_VIDEO_MT9P012) || defined(CONFIG_VIDEO_MT9P012_MODULE)
{
I2C_BOARD_INFO("mt9p012", MT9P012_I2C_ADDR),
.platform_data = &sdp3430_mt9p012_platform_data,
},
#ifdef CONFIG_VIDEO_DW9710
{
I2C_BOARD_INFO(DW9710_NAME, DW9710_AF_I2C_ADDR),
.platform_data = &sdp3430_dw9710_platform_data,
},
#endif
#endif
#endif

#if defined(CONFIG_VIDEO_OV3640) || defined(CONFIG_VIDEO_OV3640_MODULE)
{
I2C_BOARD_INFO("ov3640", OV3640_I2C_ADDR),
//.platform_data = &sdp3430_ov3640_platform_data,
.platform_data = &devkit8000_ov3640_platform_data,
},
#endif
};
/*
#define TWL4030_VAUX4_DEV_GRP 0x23
#define TWL4030_VAUX4_DEDICATED 0x26

static int __init omap3430_i2c_init(void)
{
omap_register_i2c_bus(1, 2600, sdp3430_i2c_boardinfo,
ARRAY_SIZE(sdp3430_i2c_boardinfo));
omap_register_i2c_bus(2, 400, sdp3430_i2c_boardinfo_2,
ARRAY_SIZE(sdp3430_i2c_boardinfo_2));
omap_register_i2c_bus(3, 400, NULL, 0);
return 0;
}
*/
static int __init devkit8000_i2c_init(void)
{
omap_register_i2c_bus(1, 2600, devkit8000_i2c_boardinfo,
ARRAY_SIZE(devkit8000_i2c_boardinfo));
omap_register_i2c_bus(2, 400, devkit8000_i2c_boardinfo_2,
ARRAY_SIZE(devkit8000_i2c_boardinfo_2));
/* Bus 3 is attached to the DVI port where devices like the pico DLP
* projector don't work reliably with 400kHz */
omap_register_i2c_bus(3, 100, NULL, 0);
return 0;
}