Initialize
[sdk/emulator/qemu.git] / tizen / src / dbi_parser.c
1 /*
2  * Emulator
3  *
4  * Copyright (C) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact:
7  * DoHyung Hong <don.hong@samsung.com>
8  * SeokYeon Hwang <syeon.hwang@samsung.com>
9  * Hyunjun Son <hj79.son@samsung.com>
10  * SangJin Kim <sangjin3.kim@samsung.com>
11  * MunKyu Im <munkyu.im@samsung.com>
12  * KiTae Kim <kt920.kim@samsung.com>
13  * JinHyung Jo <jinhyung.jo@samsung.com>
14  * SungMin Ha <sungmin82.ha@samsung.com>
15  * JiHye Kim <jihye1128.kim@samsung.com>
16  * GiWoong Kim <giwoong.kim@samsung.com>
17  * YeongKyoon Lee <yeongkyoon.lee@samsung.com>
18  * DongKyun Yun <dk77.yun@samsung.com>
19  *
20  * This program is free software; you can redistribute it and/or
21  * modify it under the terms of the GNU General Public License
22  * as published by the Free Software Foundation; either version 2
23  * of the License, or (at your option) any later version.
24  *
25  * This program is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28  * GNU General Public License for more details.
29  *
30  * You should have received a copy of the GNU General Public License
31  * along with this program; if not, write to the Free Software
32  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
33  *
34  * Contributors:
35  * - S-Core Co., Ltd
36  *
37  */
38
39 #include <string.h>
40 #include <stdlib.h>
41 #ifndef _WIN32
42 #include <linux/limits.h>
43 #else
44 #include <limits.h>
45 #endif
46 #include <unistd.h>
47 #include <assert.h>
48 #include "dbi_parser.h"
49
50 #include "debug_ch.h"
51
52 //DEFAULT_DEBUG_CHANNEL(tizen);
53 MULTI_DEBUG_CHANNEL(tizen, dbi_parser);
54
55
56 static int  rotate_keycode[4];
57
58
59 /**
60   * @brief      show DBI info
61   * @return     void
62   */
63 static void print_parse_info(void)
64 {
65     int i,j;
66     TRACE(" PHONE.mode_cnt =%d \n",PHONE.mode_cnt );
67
68     for( i=0;i< PHONE.mode_cnt ; i ++) {
69         for(j=0;j < PHONE.mode[i].lcd_list_cnt ; j ++ ) {
70             TRACE(" PHONE.mode[%d].lcd_list[%d].id =%d \n",i,j,PHONE.mode[i].lcd_list[j].id);
71             TRACE(" PHONE.mode[%d].lcd_list[%d].bitsperpixel =%d \n",i,j,PHONE.mode[i].lcd_list[j].bitsperpixel);
72             TRACE(" PHONE.mode[%d].lcd_list[%d].nonstd =%d \n",i,j,PHONE.mode[i].lcd_list[j].nonstd);
73             TRACE(" PHONE.mode[%d].lcd_list[%d].lcd_region.x =%d \n",i,j,PHONE.mode[i].lcd_list[j].lcd_region.x);
74             TRACE(" PHONE.mode[%d].lcd_list[%d].lcd_region.y =%d \n",i,j,PHONE.mode[i].lcd_list[j].lcd_region.y);
75             TRACE(" PHONE.mode[%d].lcd_list[%d].lcd_region.w =%d \n",i,j,PHONE.mode[i].lcd_list[j].lcd_region.w);
76             TRACE(" PHONE.mode[%d].lcd_list[%d].lcd_region.h =%d \n",i,j,PHONE.mode[i].lcd_list[j].lcd_region.h);
77         }
78     }
79
80 }
81
82 static int dbi_atoi(xmlNode *node, const char *field)
83 {
84     xmlChar *xml_str = xmlGetProp(node, (const xmlChar*)field);
85     int val = 0;
86     if (xml_str) {
87         val = atoi((const char*) xml_str);
88         xmlFree(xml_str);
89     }
90     return val;
91 }
92
93 static int dbi_load_mode_list(xmlNode *child_node, mode_list *ml, const gchar *dbi_path)
94 {
95     xmlNode *region_child_node;
96     xmlNode *region_node;
97     xmlNode *event_node;
98     xmlNode *event_value;
99     xmlNode *image_node;
100     int led_chk_cnt = 0;
101     int lcd_chk_cnt = 0;
102     int key_map_chk_cnt = 0;
103     int event_value_chk_cnt = 0;
104     int event_info_chk_cnt = 0;
105     char *pSrc_Buf;
106     char *pDes_Buf = NULL;
107     int event_info_index = -1;
108
109     // mode
110     ml->id = dbi_atoi(child_node, "id");
111     pSrc_Buf = (char *)xmlGetProp(child_node, (const xmlChar *)"name");
112     ml->name = strdup(pSrc_Buf);
113     xmlFree(pSrc_Buf);
114
115     // mode children : image_list, region, lcd_list,
116     // led_list, key_map_list
117     for (region_node = child_node->children; region_node != NULL; region_node = region_node->next) {
118         if (region_node->type != XML_ELEMENT_NODE)
119             continue;
120
121         // image_list
122         if (!xmlStrcmp(region_node->name, (const xmlChar *)"image_list")) {
123             for (image_node = region_node->children; image_node != NULL; image_node = image_node->next) {
124                 if (image_node->type != XML_ELEMENT_NODE)
125                     continue;
126                 if (!xmlStrcmp(image_node->name, (const xmlChar *) "main_image")) {
127                     pSrc_Buf = (char *) xmlNodeGetContent(image_node);
128
129                     pDes_Buf = g_strdup_printf("%s/%s", dbi_path, pSrc_Buf);
130                     TRACE(" main_image = %s \n", pDes_Buf);
131                     xmlFree(pSrc_Buf);
132                     ml->image_list.main_image = pDes_Buf;
133                 }
134
135                 else if (!xmlStrcmp(image_node->name, (const xmlChar *) "keypressed_image")) {
136                     pSrc_Buf = (char *) xmlNodeGetContent(image_node);
137
138                     pDes_Buf = g_strdup_printf("%s/%s", dbi_path, pSrc_Buf);
139                     TRACE( " keypressed_image = %s \n", pDes_Buf);
140                     xmlFree(pSrc_Buf);
141                     ml->image_list.keypressed_image = pDes_Buf;
142                 }
143
144                 else if (!xmlStrcmp(image_node->name, (const xmlChar *) "led_main_image")) {
145                     pSrc_Buf = (char *) xmlNodeGetContent(image_node);
146
147                     pDes_Buf = g_strdup_printf("%s/%s", dbi_path, pSrc_Buf);
148                     TRACE( " led_main_image = %s \n", pDes_Buf);
149                     xmlFree(pSrc_Buf);
150                     ml->image_list.led_main_image = pDes_Buf;
151                 }
152
153                 else if (!xmlStrcmp(image_node->name, (const xmlChar *) "led_keypressed_image")) {
154                     pSrc_Buf = (char *) xmlNodeGetContent(image_node);
155
156                     pDes_Buf = g_strdup_printf("%s/%s", dbi_path, pSrc_Buf);
157                     TRACE( " led_keypressed_image = %s \n", pDes_Buf);
158                     xmlFree(pSrc_Buf);
159                     ml->image_list.led_keypressed_image = pDes_Buf;
160                 }
161                 /* To identify dual display split area image(middle bar) */
162                 else if (!xmlStrcmp(image_node->name, (const xmlChar *) "splitted_screen_image"))
163                 {
164                     pSrc_Buf = (char *) xmlNodeGetContent(image_node);
165
166                     pDes_Buf = g_strdup_printf("%s/%s", dbi_path, pSrc_Buf);
167                     TRACE( " ^^^^^^^^^^^^^^^^^^^^^^^^ = %s \n", pDes_Buf);
168                     xmlFree(pSrc_Buf);
169                     ml->image_list.splitted_area_image = pDes_Buf;
170                 }
171             }
172         }
173         // region check
174         else if (!xmlStrcmp(region_node->name, (const xmlChar *)"region")) {
175             ml->REGION.x = dbi_atoi(region_node, "left");
176             ml->REGION.y = dbi_atoi(region_node, "top");
177             ml->REGION.w = dbi_atoi(region_node, "width");
178             ml->REGION.h = dbi_atoi(region_node, "height");
179         }
180         // lcd_list
181         else if (!xmlStrcmp(region_node->name, (const xmlChar *)"lcd_list")) {
182             for (image_node = region_node->children; image_node != NULL; image_node = image_node->next) {
183                 if (image_node->type != XML_ELEMENT_NODE)
184                     continue;
185                 if (!xmlStrcmp(image_node->name, (const xmlChar *)"lcd")) {
186                     lcd_list_data *ll = &ml->lcd_list[lcd_chk_cnt];
187                     ll->id = dbi_atoi(image_node, "id");
188                     ll->bitsperpixel = dbi_atoi(image_node, "bitsperpixel");
189                     // hwjang add for overlay
190                     ll->nonstd = dbi_atoi(image_node, "nonstd");
191                     // node 5
192                     for (region_child_node = image_node->children; region_child_node != NULL; region_child_node = region_child_node->next) {
193                         if (region_node->type != XML_ELEMENT_NODE)
194                             continue;
195                         if (!xmlStrcmp(region_child_node->name, (const xmlChar *) "region")) {
196                             xmlChar *scale = NULL;
197                             ll->lcd_region.x = dbi_atoi(region_child_node, "left");
198                             ll->lcd_region.y = dbi_atoi(region_child_node, "top");
199                             ll->lcd_region.w = dbi_atoi(region_child_node, "width");
200                             ll->lcd_region.h = dbi_atoi(region_child_node, "height");
201
202                             scale = xmlGetProp(region_child_node, (const xmlChar *) "scale");
203                             if (scale != NULL)
204                                 ll->lcd_region.s = atof((char *)scale);
205                             else
206                                 ll->lcd_region.s = 1.0;
207
208                             ll->lcd_region.split = dbi_atoi(region_child_node,  "split");
209                         }
210                     }
211                     lcd_chk_cnt++;  // lcd list count
212                     // value
213                     ml->lcd_list_cnt = lcd_chk_cnt;
214                 }
215             }
216         }
217         // led_list
218         else if (!xmlStrcmp(region_node->name, (const xmlChar *)"led_list")) {
219             for (image_node = region_node->children; image_node != NULL; image_node = image_node->next) {
220                 if (region_node->type != XML_ELEMENT_NODE)
221                     continue;
222                 if (!xmlStrcmp(image_node->name, (const xmlChar *)"led")) {
223                     ml->led_list[led_chk_cnt].led_region.x = dbi_atoi(image_node, "left");
224                     ml->led_list[led_chk_cnt].led_region.y = dbi_atoi(image_node, "top");
225                     ml->led_list[led_chk_cnt].led_region.w = dbi_atoi(image_node, "width");
226                     ml->led_list[led_chk_cnt].led_region.h = dbi_atoi(image_node, "height");
227                     // node 5
228                     for (region_child_node = image_node->children; region_child_node != NULL; region_child_node = region_child_node->next) {
229                         if (region_child_node->type == XML_ELEMENT_NODE && !xmlStrcmp(region_child_node->name, (const xmlChar *)
230                                                                                       "led_color")) {
231                             pSrc_Buf = (char *) xmlGetProp(region_child_node, (const xmlChar *)"id");
232                             ml->led_list[led_chk_cnt].id = strdup(pSrc_Buf);
233                             xmlFree(pSrc_Buf);
234                             pSrc_Buf = (char *) xmlGetProp(region_child_node, (const xmlChar *)"name");
235                             ml->led_list[led_chk_cnt].name = strdup(pSrc_Buf);
236                             xmlFree(pSrc_Buf);
237                             pSrc_Buf = (char *) xmlGetProp(region_child_node, (const xmlChar *) "imagepath");
238                             ml->led_list[led_chk_cnt].imagepath = strdup(pSrc_Buf);
239                             xmlFree(pSrc_Buf);
240                         }
241                     }
242                     led_chk_cnt++;  // led list count
243                     ml->led_list_cnt = led_chk_cnt;
244                 }
245             }
246         }
247         // key_map_list
248         else if (!xmlStrcmp(region_node->name, (const xmlChar *) "key_map_list")) {
249             for (image_node = region_node->children; image_node != NULL; image_node = image_node->next) {
250                 if (image_node->type != XML_ELEMENT_NODE)
251                     continue;
252                 if (!xmlStrcmp(image_node->name, (const xmlChar *) "key_map")) {
253                     // node5
254                     for (region_child_node = image_node->children; region_child_node != NULL; region_child_node = region_child_node->next) {
255                         if (region_node->type != XML_ELEMENT_NODE)
256                             continue;
257                         if (!xmlStrcmp(region_child_node->name, (const xmlChar *) "region")) {
258                             region *reg = &ml->key_map_list[key_map_chk_cnt].key_map_region;
259                             reg->x = dbi_atoi(region_child_node, "left");
260                             reg->y = dbi_atoi(region_child_node, "top");
261                             reg->w = dbi_atoi(region_child_node, "width");
262                             reg->h = dbi_atoi(region_child_node, "height");
263                         }
264
265                         else if (!xmlStrcmp(region_child_node->name, (const xmlChar *) "event_info")) {
266                             // array[0] VKS_KEY_PRESSED
267                             // array[1] VKS_KEY_RELEASED
268                             char *temp = (char *) xmlGetProp(region_child_node, (const xmlChar *) "status");
269                             if (strcmp(temp, KEY_PRESS) == 0) {
270                                 event_info_index = 0;
271                             } else if (strcmp(temp, KEY_RELEASE) == 0) {
272                                 event_info_index = 1;
273                             } else
274                                 return -1;
275                             xmlFree(temp);
276
277                             for (event_node = region_child_node->children; event_node != NULL; event_node = event_node->next) {
278                                 event_info_data *ei = &ml->key_map_list[key_map_chk_cnt].event_info[event_info_index];
279                                 if (region_node->type != XML_ELEMENT_NODE)
280                                     continue;
281
282                                 // event_id
283                                 if (!xmlStrcmp(event_node->name, (const xmlChar *) "event_id")) {
284                                     pSrc_Buf = (char *) xmlNodeGetContent(event_node);
285                                     ei->event_id = strdup(pSrc_Buf);
286                                     xmlFree(pSrc_Buf);
287                                 }
288                                 // event_value
289                                 else if (!xmlStrcmp(event_node->name, (const xmlChar *) "event_value")) {
290                                     // node 7
291                                     for (event_value = event_node->children; event_value != NULL; event_value = event_value->next) {
292                                         event_value_data *ev = &ei->event_value[event_value_chk_cnt];
293                                         if (region_node->type != XML_ELEMENT_NODE)
294                                             continue;
295                                         if (!xmlStrcmp(event_value->name, (const xmlChar *) "key_code")) {
296                                             ev->key_code = atoi((char *) xmlNodeGetContent(event_value));
297                                         }
298
299                                         else if (!xmlStrcmp(event_value->name, (const xmlChar *) "key_name")) {
300                                             pSrc_Buf = (char *) xmlNodeGetContent(event_value);
301                                             ev->key_name = strdup(pSrc_Buf);
302                                             xmlFree(pSrc_Buf);
303                                             event_value_chk_cnt++;
304                                             ei->event_value_cnt = event_value_chk_cnt;
305                                         }
306                                     }
307                                 }
308                                 // keyboard
309                                 else if (!xmlStrcmp(event_node->name, (const xmlChar *) "keyboard")) {
310                                     pSrc_Buf = (char *) xmlNodeGetContent(event_node);
311                                     ei->keyboard = strdup(pSrc_Buf);
312                                     xmlFree(pSrc_Buf);
313                                 }
314                             }
315                             event_value_chk_cnt = 0;
316                             event_info_chk_cnt++;
317                             ml->key_map_list[key_map_chk_cnt].event_info_cnt = event_info_chk_cnt;
318                         }
319
320                         else if (!xmlStrcmp(region_child_node->name, (const xmlChar *) "tooltip")) {
321                             pSrc_Buf = (char *) xmlNodeGetContent(region_child_node->children);
322                             if (pSrc_Buf == NULL) {
323                                 ml->key_map_list[key_map_chk_cnt].tooltip = NULL;
324                             } else {
325                                 ml->key_map_list[key_map_chk_cnt].tooltip = strdup(pSrc_Buf);
326                                 xmlFree(pSrc_Buf);
327                             }
328                         }
329
330                     }
331                     key_map_chk_cnt++;  // key_map list
332                     // count
333                     ml->key_map_list_cnt = key_map_chk_cnt;
334                 }
335             }
336         }
337     }
338     return 0;
339 }
340
341 static int dbi_parse_mode_selection(xmlNode *cur_node, const gchar * filename, PHONEMODELINFO * pDeviceData)
342 {
343     xmlNode *child_node;
344     int mode_chk_cnt = 0;
345
346     gchar* dbi_path = g_path_get_dirname(filename);
347
348     // mode_section
349     for (child_node = cur_node->children; child_node != NULL; child_node = child_node->next) {
350         if (child_node->type == XML_ELEMENT_NODE && !xmlStrcmp(child_node->name, (const xmlChar *)"mode")) {
351             assert (mode_chk_cnt < MODE_MAX);
352             dbi_load_mode_list(child_node, &pDeviceData->mode[mode_chk_cnt], dbi_path);
353             mode_chk_cnt++; // mode count check
354             pDeviceData->mode_cnt = mode_chk_cnt;
355         }
356
357         // Cover mode paser !!!!!
358         if (child_node->type == XML_ELEMENT_NODE && !xmlStrcmp(child_node->name, (const xmlChar *)"cover_mode")) {
359             assert(pDeviceData->cover_mode_cnt == 0);
360             pDeviceData->cover_mode_cnt++;
361             dbi_load_mode_list(child_node, &pDeviceData->cover_mode, dbi_path);
362         }
363     }
364
365     g_free(dbi_path);
366     return 0;
367 }
368
369 /**
370   * @brief      parse DBI file which describes specific phone model outlook
371   * @param  filename:   dbi file name
372   * @param  pDeviceData: pointer to structure saving the device information
373   * @return     success : 0 failure : -1
374   */
375 int parse_dbi_file(const gchar * filename, PHONEMODELINFO * pDeviceData)
376 {
377     // event menu
378     int event_menu_cnt = 0;
379     int event_list_cnt = 0;
380
381     // malloc buffer
382     char *pSrc_Buf;
383
384     if (filename == NULL || strlen(filename) == 0 || pDeviceData == NULL) {
385         WARN( "Please input skin path (%s)\n", filename);
386         return -1;
387     }
388
389     // initialize
390     xmlNode *cur_node, *child_node; // node1, node2
391     xmlNode *region_node;       // node3
392
393     // open XML Document
394     xmlDocPtr doc;
395     doc = xmlParseFile(filename);
396
397     if (doc == NULL) {
398         WARN( "can't parse file %s\n", filename);
399         return -1;
400     }
401     // XML root
402     WARN( "start to parse file %s\n", filename);
403
404     xmlNode *root = NULL;
405     root = xmlDocGetRootElement(doc);
406
407     int run_step = 0;
408     // Must have root element, a name and the name must be "PHONEMODELINFO"
409     if (!root || !root->name || xmlStrcmp(root->name, (const xmlChar *)"device_info")) {
410         xmlFreeDoc(doc);
411         return FALSE;
412     }
413     // PHONEMODELINFO children
414     for (cur_node = root->children; cur_node != NULL; cur_node = cur_node->next) {
415
416         /* 1. key mode parsing (old(XOcean), new(mirage))) in 20090330 */
417
418         if (cur_node->type == XML_ELEMENT_NODE && !xmlStrcmp(cur_node->name, (const xmlChar *)"key_mode")) {
419
420         /* 1. 1 child_mode */
421
422             for (child_node = cur_node->children; child_node != NULL; child_node = child_node->next) {
423                 /* 1. 2 key */
424
425                 if (child_node->type == XML_ELEMENT_NODE && !xmlStrcmp(child_node->name, (const xmlChar *)"key")) {
426
427                     /* 1. 3 key type parsing */
428
429                     rotate_keycode[run_step] = dbi_atoi(child_node, "code");
430                     run_step++;
431                 }
432             }
433             run_step = 0;
434         }
435         /* New node for enabling dual_display */
436         if (cur_node->type == XML_ELEMENT_NODE && !xmlStrcmp(cur_node->name, (const xmlChar *)"device_information"))
437         {
438             for (child_node = cur_node->children; child_node != NULL; child_node = child_node->next)
439             {
440                 if (child_node->type == XML_ELEMENT_NODE && !xmlStrcmp(child_node->name, (const xmlChar *)"device_name"))
441                 {
442                     strcpy(pDeviceData->model_name,(char *)xmlGetProp(child_node, (const xmlChar *)"name"));
443                     if (strcmp(pDeviceData->model_name,"dual_display")==0)
444                     {
445                         pDeviceData->dual_display = 1;
446                     }
447                     else
448                     {
449                         pDeviceData->dual_display = 0;
450                     }
451                     run_step++;
452                 }
453             }
454
455             run_step = 0;
456         }
457
458         if (cur_node->type == XML_ELEMENT_NODE && !xmlStrcmp(cur_node->name, (const xmlChar *)"mode_section")) {
459             int r;
460             r = dbi_parse_mode_selection(cur_node, filename, pDeviceData);
461             if (r < 0)
462                 return r;   /* FIXME: leak memory here */
463         }
464
465         // event menu start
466         if (cur_node->type == XML_ELEMENT_NODE && !xmlStrcmp(cur_node->name, (const xmlChar *)"event_menu")) {
467             for (child_node = cur_node->children; child_node != NULL; child_node = child_node->next) {
468                 if (child_node->type == XML_ELEMENT_NODE && !xmlStrcmp(child_node->name, (const xmlChar *)"menu")) {
469                     event_menu_list *em = &pDeviceData->event_menu[event_menu_cnt];
470                     pSrc_Buf = (char *)xmlGetProp(child_node, (const xmlChar *)"name");
471                     g_strlcpy(em->name, pSrc_Buf, sizeof em->name);
472                     xmlFree(pSrc_Buf);
473
474                     // event id
475                     for (region_node = child_node->children; region_node != NULL; region_node = region_node->next) {
476                         if (region_node->type == XML_ELEMENT_NODE && !xmlStrcmp(region_node->name, (const xmlChar *)"event")) {
477                             event_prop *ep = &em->event_list[event_list_cnt];
478                             pSrc_Buf = (char *)xmlGetProp(region_node, (const xmlChar *) "id");
479                             g_strlcpy(ep->event_eid, pSrc_Buf, sizeof ep->event_eid);
480                             xmlFree(pSrc_Buf);
481
482                             pSrc_Buf = (char *)xmlGetProp(region_node, (const xmlChar *) "value");
483                             g_strlcpy(ep->event_evalue, pSrc_Buf, sizeof ep->event_evalue);
484                             xmlFree(pSrc_Buf);
485
486                             event_list_cnt++;
487                             em->event_list_cnt = event_list_cnt;
488                         }// endif
489                     }// end for
490                     event_menu_cnt++;
491                     pDeviceData->event_menu_cnt = event_menu_cnt;
492                     event_list_cnt = 0;
493                 }
494             }// end for
495         }
496     }
497
498
499     print_parse_info();
500     // free document
501     xmlFreeDoc(doc);
502
503     // free the global variables that way have been allocated by the
504     // parser.
505     xmlCleanupParser();
506     return 1;
507 }
508
509 static void free_modelist(mode_list *ml)
510 {
511     int i;
512
513     if (ml->name) {
514         free(ml->name);
515     }
516
517     g_free(ml->image_list.main_image);
518     g_free(ml->image_list.keypressed_image);
519     g_free(ml->image_list.led_main_image);
520     g_free(ml->image_list.led_keypressed_image);
521     g_free(ml->image_list.splitted_area_image);
522
523     for (i = 0; i < ml->led_list_cnt; i++) {
524         if (ml->led_list[i].name) {
525             free(ml->led_list[i].name);
526         }
527         if (ml->led_list[i].imagepath) {
528             free(ml->led_list[i].imagepath);
529         }
530     }
531
532     for (i = 0; i < ml->key_map_list_cnt; i++) {
533         if (ml->key_map_list[i].tooltip) {
534             free(ml->key_map_list[i].tooltip);
535         }
536         //todo:
537     }
538 }
539
540 int free_dbi_file(PHONEMODELINFO *pDeviceData)
541 {
542     int i;
543
544     for (i = 0; i < pDeviceData->mode_cnt; i++) {
545         mode_list *ml = &pDeviceData->mode[i];
546         free_modelist(ml);
547     }
548
549     return 0;
550 }
551
552 /**
553  * vim:set tabstop=4 shiftwidth=4 foldmethod=marker wrap:
554  *
555  */
556