Wi-Fi 프린팅 지원 Canon PIXMA MG2990 개봉기

흔치않은일상 2015. 4. 17. 00:01

지금까지 컴퓨터는 몇 번이고 바꾸어 오면서도 프린터기는 약 10년전 즈음부터 사용했던 HP 복합기를 사용해 왔었습니다.

지금도 인쇄가 되고 있기는 합니다만

워낙 연세를 많이 잡수시다 보니 나중에는 인쇄 속도가 많이 떨어지는 모습을 보이고 있었습니다.


그래서 이참에 새로운 프린터기를 한 대 장만해볼까 해서 알아보았는데

요즘은 스마트 시대에 도래하면서 NFC 기능을 사용한 프린터기도 선보이는 추세인데요.

그 중 Wi-Fi를 활용하여 무선으로 프린팅이 가능한 Canon사의 PIXMA MG2900을 구매해 보았습니다.



인터넷으로 주문한 PIXMA MG2990 기기가 도착하였습니다.



자세히 살펴보니 제가 원하는 기능이기도 한 Wi-Fi 무선 프린팅을 지원하고 있는 것을 확인하실 수 있습니다.



드디어 오픈~1



열자마자 내용물로 거대한 그림을 활용한 설시 설명서를 보실 수 있습니다.

이 순서대로 사용하신다면 간편하게 바로 설치하여 사용하실 수 있습니다.



설치설명서를 꺼낸 후 내부의 모습입니다.



드디어 내용물을 꺼내봅니다!



바로 위에는 취급설명서와 드라이브 설치CD를 확인하실 수 있습니다.



프린터 박스 아랫쪽에 길다란 노란 포장지가 보이는데요 이것의 정체는 무엇일까요?



내용을 확인해 보니



다름아닌 전원코드와 USB 연결선이 들어있었습니다.

이와중에 왼쪽 오른쪽에는 정체불명의 플라스틱 포장물이 보이는데요.



아하!

컬러잉크와 흑색잉크가 각각 동봉이 되어 있었던 것이었네요!!



드디어 본론이로군요! MG2990를 살펴보도록 합시다!!



오호



하얀 빛의 디자인이 상당히 매력적입니다!



용지 삽입 부분을 개방해 보았습니다.



스캐너 부분도 살펴봅시다.

구조는 다른 복합기와는 딱히 차이는 없어보이는군요.



프린터기의 내부입니다.

잉크를 넣을 수 있는 슬롯 2개가 보이는데요

왼쪽 슬롯이 컬러잉크, 오른쪽 슬롯이 검은잉크를 삽입하는 부분입니다.



MG2990의 전원은 어댑터가 보시는 바와 같이 본체에 붙어있는 방식입니다.

그덕에 덩치큰 어댑터를 따로 공간 낭비없이 간편하게 해결한 발상이 참 대단합니다.



드디어 설치 완료!



전원을 키니 보시는 바와 같이 무선모드도 동시에 켜지는 것으로 보입니다.



그 와중에 경고 램프가 켜져있는데요

생각해보니 아직 잉크를 설치하지 않았더군요.



역시나 내부에는 잉크가 삽입되어 있지 않습니다.



앞에서 보았던 이 두 잉크를 꺼내봅니다.



꺼내자마자 샷 한장 더!



앞에서 설명드린 거와 같이

슬롯의 왼쪽에는 컬러, 오른쪽에는 검은 잉크를 넣어주세요~!



두 잉크가 정상적으로 설치되었습니다!



다시 경고 램프는 문제 없다는 듯 조용히 꺼지는군요.



인쇄를 위해 용지를 넣어주고



종이 밭침대도 늘려주면



드디어 설치 완료!



이렇게 용지가 인쇄되기를 학수고대 해준다면



보시는 바와 같이 프린터가 정상적으로 용지를 인쇄하는 거을 확인하실 수 있습니다!


 혹시 Canon PIXMA MG2900 시리즈의 무선 상태에서 드라이버를 설치하는 방법에 대해 알고 싶으신 분은 아래 링크를 참조해 주시길 바랍니다.


http://elecs.tistory.com/101

300x250

세월호 참사 1주기 합동분향소 주변풍경

흔치않은일상 2015. 4. 16. 19:13

작년 이맘때 즈음,

저는 학교 PC실 한구석에서 작업을 하고 있었습니다.

잠시 인터넷 포털사이트를 접속하였을 때 보게 된 보게된 사진 한 장.

바로 세월호가 침몰하여 배의 일부분만 바다 위에 떠있던 장면이었습니다.


그 안에 수많은 학생들이 있었다는 사실을 접하게 되었을 때 경악을 금치 못하였습니다.

부디 배 안에 갇힌 학생들이 단 한명이라도 좋으니 무사히 구출되길 간절히 바랐지만

그러한 바람을 마음에 품고 지낸지 벌써 1년이라는 시간이 지나버렸습니다.


여전히 세월호 유족분들은 진상규명을 외치며 거리에 나와계시지만

1주기가 된 지금까지도 해결되지 못한 점이 참으로 마음이 아픕니다.




오늘 구글 메인 화면에는 세월호 유족들을 애도하는 검은 리본이 우리를 반기고 있군요.

이를 보고 오늘이 1주기임을 느낄 수 있었습니다.

아이들이 배와 함께 가라앉았던 사건이 불과 어제의 일인것만 같았던데 말이지요.


그렇게 바쁘게 시간이 흘러 1주기가 된 오늘 세월호 합동분향소에 다녀왔습니다.






안산 세월호 합동분향소는 회랑유원지에 위치해 있습니다.

지도를 보니 4호선 초지역에서 걸어서 갈 만한 거리에 있길래 직접 발걸음을 옮겨보기로 하였습니다.



역에서 내리자마자 제 눈앞에 등장한 현수막입니다.

여전히 부모는 세월호에 오른 아이들을 한없이 기다리고 있습니다.



열심히 걷다보니 어느덧 초지역에서 이만큼 걸어왔습니다.



정신없는 공사현장을 지나 앞으로 쭈욱 나아가다 보면 사거리가 눈에 보일 겁니다.



안산시민이 건 현수막입니다.

현수막 뒤로는 합동분향소 추모행사에 참가하러 가는 아이들이 눈에 보입니다.



아이들의 가슴에는 자신들이 직접 만든 리본을 달고 있더군요.



한 아이의 가방에 보이는 세월호 문구가 제 마음을 짠하게 만들고 있습니다.



가는 길마다 보이는 현수막들은

여전히 작년 이맘때 즈음의 사건을 잊지 않게 해줍니다.



작년 11월 세월호 실종자 수식이 종료된 이후 실종자 9명은 아직까지도 가족들 품에 돌아오지 못하고 있습니다.

실종자 9분들이 모두 가족의 품으로 돌아가기를 간절히 바랍니다.



드디어 합동분양소 입구에 도착하였습니다.



세월호 사고 희생자 합동분향소 전면입니다.



그날을 잊지 않기 위해 사람들이 그린 그림들이 전시되고 있습니다.



그 많은 그림들 중 갖아 인상에 남는 그림중 하나였습니다.

광화문 앞에서 단식농성을 벌이던 유민아빠의 모습을 보고

순간 눈시울이 붉어집니다.



1주기 추모식 행사가 한창 준비중인 모습입니다.

날씨가 좋지 않던데 무사히 진행되었으면 좋겠습니다.




분향소 앞에서 피켓을 들고 서있는 모습입니다.

세월호 참사 1주기가 되는 지금까지도 유가족들은 여전히 밝혀지지 않은 진상규명을 정부에게 요청하고 있습니다.



분향소 한쪽에서는 관련 추모 행사의 포스터가 걸려있는 것을 볼 수 있었습니다.



합동분향소 인근에 위치한 호수입니다.,

아이들이 살아있다면 이 곳에서 정다운 이야기를 하며 하루를 보냈을텐데...



가족들은 여전히 세월호 침몰의 정확한 원인을 알지 못합니다.



그렇기에 이렇게 정부에게 진상규명을 요구하지만

세월호의 진상은 여전히 밝혀지지 않은채 제자리 걸음을 하고 있습니다.



인근 주민센터에는 세월호 노란리본 깃발이 태극기와 함께 조기로 걸려있었습니다.





호수에서 단원고등학교로 가던 길에 갑자기 소나기가 내리기 시작합니다.

지금 아이들이 살아있었다면 이 길을 자기 친구들과 함께 해맑은 표정으로 학교에 가고 있을 모습이 문듯 스쳐갑니다.



2015년 4월 16일 단원고등학교 정문의 풍경입니다.

학교 분위기는 작년과 크게 달라보이지 않습니다.



정문 건너편에서의 풍경입니다. 빗줄기가 제법 굵어졌습니다.



학교 건너편에서는 세월호 관련 시민단체 분들이 모여 희생자 분의 이야기를 듣고 있습니다.

단원고 마지막 생존자가 물속에서 나오는 순간에 대한 이야기를 들으니

배 안에서 짫은 인생을 마감한 아이들의 슬픈 얼굴이 떠올랐습니다.



그렇게 합동분향소를 떠나 버스정류장에 도착한 오늘

2015년 4월 16일 목요일입니다......



▶◀ 세월호에 탑승하였다가 희생된 분들의 명복을 빕니다

300x250

안드로이드 프레임워크 프로그래밍(19) [Native 단계의 ServiceManager 동작원리]

안드로이드/프레임워크 2015. 4. 10. 00:33

 지금까지 우리는 안드로이드 내의 System service가 ServiceManager에 의해 관리되고 있음을 알 수 있었습니다. IServiceManager를 통해 BpServiceManager를 생성하여 Binder를 통해 서비스를 등록하거나 찾는 과정 또한 확인했습니다.


/frameworks/native/libs/binder/IServiceManager.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
android::sp<IServiceManager> IServiceManager::asInterface(                           
    const android::sp<android::IBinder>& obj)                       
    {
        android::sp<IServiceManager> intr;
        if (obj != NULL) {
            intr = static_cast<IServiceManager*>(
                obj->queryLocalInterface(
                    IServiceManager::descriptor).get());
        if (intr == NULL) {
            intr = new BpServiceManager(obj);
        }
    }
    return intr;
cs


이러한 과정을 거치는 한 편으로는 이런 생각을 가지신 분들도 계시리라 생각합니다.


"BpServiceManager와 BnServiceManager가 존재한다면 ServiceManager 클래스도 존재하지 않을까?"


 대부분의 분들이라면 분명 있으리라 생각하실 겁니다. 그러나 놀랍게도 ServiceManager는 Java 단계에서는 클래스가 존재합니다만 Native 단계에서는 ServiceManager 클래스를 확인하실 수 없습니다. 그렇다면 Native 단계에서는 ServiceManager가 사용되지 않는걸까요?

 사실 Native 단계에서 ServiceManager는 daemon 프로세스와 같이 백그라운드에서 지속적으로 동작하는 프로세스로 존재합니다. 비록 ServiceManager라는 이름은 아니지만 Binder를 등록하거나 검색을 할 수 있는 기능을 갖추어 놓고 있습니다. ServiceManager는 다음과 같은 파일들로 구성되어 있습니다.


/frameworks/native/cmds/servicemanager/binder.h

/frameworks/native/cmds/servicemanager/binder.c

/frameworks/native/cmds/servicemanager/service_manager.c


 시작하기에 앞서 Android.mk에 설정된 모습을 보도록 하겠습니다.

/frameworks/native/cmds/servicemanager/Android.mk

1
2
3
4
5
6
7
8
9
10
11
12
LOCAL_PATH:= $(call my-dir)
 
#include $(CLEAR_VARS)
#LOCAL_SRC_FILES := bctest.c binder.c
#LOCAL_MODULE := bctest
#include $(BUILD_EXECUTABLE)
 
include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SRC_FILES := service_manager.c binder.c
LOCAL_MODULE := servicemanager
include $(BUILD_EXECUTABLE)
cs


 이제 ServiceManager가 실행되는 모습을 보도록 합시다.


/frameworks/native/cmds/servicemanager/service_manager.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void *svcmgr_handle;
 
int main(int argc, char **argv)
{
    struct binder_state *bs;
    void *svcmgr = BINDER_SERVICE_MANAGER;
 
    bs = binder_open(128*1024);
 
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }
 
    svcmgr_handle = svcmgr;
    binder_loop(bs, svcmgr_handler);
    return 0;
}
cs


/frameworks/native/cmds/servicemanager/binder.h

1
2
/* the one magic object */
#define BINDER_SERVICE_MANAGER ((void*0)
cs


 처음엔 binder_state 구조체 변수와 void 변수 svcmgr이 선언됩니다. binder_state에는 이름 그대로 바인더의 상태를 저장하기 위해 사용되는 구조체임을 알 수 있습니다. 여기서 main() 함수의 내용을 하나씩 살펴보겠습니다.


    bs = binder_open(128*1024);


 binder_open() 함수가 선언되어 있고 이를 통해 binder_state 구조체를 return 하는 모습을 보이고 있습니다. binder_open() 함수를 통해 binder가 설정되며 인자로 메모리에 할당할 용량을 설정합니다.


/frameworks/native/cmds/servicemanager/binder.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
struct binder_state
{
    int fd;
    void *mapped;
    unsigned mapsize;
};
 
struct binder_state *binder_open(unsigned mapsize)
{
    struct binder_state *bs;
 
    bs = malloc(sizeof(*bs));
    if (!bs) {
        errno = ENOMEM;
        return 0;
    }
 
    bs->fd = open("/dev/binder", O_RDWR);
    if (bs->fd < 0) {
        fprintf(stderr,"binder: cannot open device (%s)\n",
                strerror(errno));
        goto fail_open;
    }
 
    bs->mapsize = mapsize;
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr,"binder: cannot map device (%s)\n",
                strerror(errno));
        goto fail_map;
    }
 
        /* TODO: check version */
 
    return bs;
 
fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return 0;
}
cs

 binder_open() 함수의 내부를 살펴보도록 합시다. open() 함수가 호출됨으로서 바딘더의 File descriptor를 얻어낸 후 mmap() 함수를 호출하여 실제 메모리에 바인더를 할당하는 작업을 진행합니다. 모든 것이 완료되면 binder_state를 저장한 구조체 변수의 포인터를 return 합니다.


binder_become_context_manager(bs)


binder_state의 값을 통하여 컨텍스트 매니저(ServiceManager)을 설정해줍니다.

/frameworks/native/cmds/servicemanager/binder.c

1
2
3
4
int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
cs

binder.h

1
#define BINDER_SET_CONTEXT_MGR _IOW('b'7int)
cs

 해당 함수는 ioctl() 함수를 사용하여 바인더의 입출력을 제어합니다.


binder_loop(bs, svcmgr_handler);


 이 함수가 실행됨으로서 ServiceManager의 실행은 loop에 들어갑니다. 즉, 시스템에 특별한 이상이 발생하지 않는 한 작동이 계속 되는 것으로 이해하시면 좋을 듯 합니다.


/frameworks/native/cmds/servicemanager/binder.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;
    unsigned readbuf[32];
 
    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;
    
    readbuf[0= BC_ENTER_LOOPER;
    binder_write(bs, readbuf, sizeof(unsigned));
 
    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (unsigned) readbuf;
 
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
 
        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }
 
        res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);
        if (res == 0) {
            ALOGE("binder_loop: unexpected reply?!\n");
            break;
        }
        if (res < 0) {
            ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
            break;
        }
    }
}
 
int binder_write(struct binder_state *bs, void *data, unsigned len)
{
    struct binder_write_read bwr;
    int res;
    bwr.write_size = len;
    bwr.write_consumed = 0;
    bwr.write_buffer = (unsigned) data;
    bwr.read_size = 0;
    bwr.read_consumed = 0;
    bwr.read_buffer = 0;
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
    if (res < 0) {
        fprintf(stderr,"binder_write: ioctl failed (%s)\n",
                strerror(errno));
    }
    return res;
}
 
 
int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uint32_t *ptr, uint32_t size, binder_handler func)
{
    int r = 1;
    uint32_t *end = ptr + (size / 4);
 
    while (ptr < end) {
        uint32_t cmd = *ptr++;
#if TRACE
        fprintf(stderr,"%s:\n", cmd_name(cmd));
#endif
        switch(cmd) {
        case BR_NOOP:
            break;
        case BR_TRANSACTION_COMPLETE:
            break;
        case BR_INCREFS:
        case BR_ACQUIRE:
        case BR_RELEASE:
        case BR_DECREFS:
#if TRACE
            fprintf(stderr,"  %08x %08x\n", ptr[0], ptr[1]);
#endif
            ptr += 2;
            break;
        case BR_TRANSACTION: {
            struct binder_txn *txn = (void *) ptr;
            if ((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) {
                ALOGE("parse: txn too small!\n");
                return -1;
            }
            binder_dump_txn(txn);
            if (func) {
                unsigned rdata[256/4];
                struct binder_io msg;
                struct binder_io reply;
                int res;
 
                bio_init(&reply, rdata, sizeof(rdata), 4);
                bio_init_from_txn(&msg, txn);
                res = func(bs, txn, &msg, &reply);
                binder_send_reply(bs, &reply, txn->data, res);
            }
            ptr += sizeof(*txn) / sizeof(uint32_t);
            break;
        }
        case BR_REPLY: {
            struct binder_txn *txn = (void*) ptr;
            if ((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) {
                ALOGE("parse: reply too small!\n");
                return -1;
            }
            binder_dump_txn(txn);
            if (bio) {
                bio_init_from_txn(bio, txn);
                bio = 0;
            } else {
                    /* todo FREE BUFFER */
            }
            ptr += (sizeof(*txn) / sizeof(uint32_t));
            r = 0;
            break;
        }
        case BR_DEAD_BINDER: {
            struct binder_death *death = (void**ptr++;
            death->func(bs, death->ptr);
            break;
        }
        case BR_FAILED_REPLY:
            r = -1;
            break;
        case BR_DEAD_REPLY:
            r = -1;
            break;
        default:
            ALOGE("parse: OOPS %d\n", cmd);
            return -1;
        }
    }
 
    return r;
}
cs


binder.h

1
2
3
4
5
6
7
8
9
10
struct binder_write_read {
 signed long write_size;
 signed long write_consumed;
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
 unsigned long write_buffer;
 signed long read_size;
 signed long read_consumed;
 unsigned long read_buffer;
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
};
cs



 제 이전 포스팅에서 Native 단계에서의 Parcel의 전송과정에 대해 다루었던 과정을 보셨던 분이라면 위의 소스코드를 읽었을 때 '앗!'이라는 생각을 하시는 분들이 계시리라 생각합니다. 그렇습니다. Parcel에서 파일이 전송되려 할 때 transact() 함수를 통해 전송되어 오는 Parcel의 값들을 처리하고 있는 것임을 본 포스팅을 통해 확실히 알게 되셨으리라 생각합니다.


 사실 위의 과정에서 좀 더 많은 설명을 해드리고 싶습니다만 글을 더 진행하기엔 포스팅의 분량도 많아질 뿐더러 내용 또한 어려워지기 때문에 이후 시간이 된다면 Parcel이 Binder와 어떻게 동작하는 지에 대해 자세히 다루어 보는 시간을 가져보도록 하겠습니다.

300x250