2 * Hardware abstraction layer for HP iPAQ H3xxx Pocket Computers
4 * Copyright 2000,2001 Compaq Computer Corporation.
6 * Use consistent with the GNU GPL is permitted,
7 * provided that this copyright notice is
8 * preserved in its entirety in all copies and derived works.
10 * COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
11 * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
12 * FITNESS FOR ANY PARTICULAR PURPOSE.
18 #include <linux/module.h>
19 #include <linux/version.h>
21 #include <linux/init.h>
23 #include <linux/delay.h>
24 #include <linux/poll.h>
25 #include <asm/uaccess.h> /* get_user,copy_to_user */
26 #include <linux/string.h>
27 #include <linux/interrupt.h>
28 #include <linux/sysctl.h>
29 #include <linux/console.h>
30 #include <linux/devfs_fs_kernel.h>
32 #include <linux/tqueue.h>
33 #include <linux/sched.h>
35 #include <linux/proc_fs.h>
36 #include <linux/apm_bios.h>
37 #include <linux/kmod.h>
39 #include <asm/hardware.h>
40 #include <asm/arch-sa1100/h3600_hal.h>
42 #define H3600_HAL_PROC_DIR "hal"
44 /* Interface supplied by the low-level microcontroller driver */
45 struct h3600_hal_ops
*h3600_hal_ops
= NULL
;
46 EXPORT_SYMBOL(h3600_hal_ops
);
48 static struct h3600_driver_ops g_driver_ops
;
51 static unsigned char flite_brightness
= 25;
52 static int max_flite_brightness
= 255;
53 static enum flite_pwr flite_power
= FLITE_PWR_ON
;
54 static int lcd_active
= 1;
55 static unsigned char screen_contrast
= 100;
59 MODULE_PARM(flite_brightness
,"b");
60 MODULE_PARM_DESC(flite_brightness
,"Initial brightness setting of the frontlight");
61 MODULE_PARM(max_flite_brightness
,"i");
62 MODULE_PARM_DESC(max_flite_brightness
,"Maximum allowable brightness setting of the frontlight");
63 MODULE_PARM(flite_power
,"i");
64 MODULE_PARM_DESC(flite_power
,"Initial power setting of the frontlight (on/off)");
65 MODULE_PARM(screen_contrast
,"i");
66 MODULE_PARM_DESC(screen_contrast
,"Initial screen contrast (for H3100 only)");
68 MODULE_AUTHOR("Andrew Christian");
69 MODULE_DESCRIPTION("Hardware abstraction layer for the iPAQ H3600");
70 MODULE_LICENSE("Dual BSD/GPL");
72 /***********************************************************************************/
73 /* General callbacks */
74 /***********************************************************************************/
76 int h3600_set_flite(enum flite_pwr pwr
, unsigned char brightness
)
78 if (0) printk("### %s: pwr=%d brightness=%d lcdactive=%d\n",
79 __FUNCTION__
, pwr
, brightness
, lcd_active
);
81 if ( brightness
> max_flite_brightness
)
82 brightness
= max_flite_brightness
;
84 /* Save the current settings */
86 flite_brightness
= brightness
;
88 return h3600_backlight_control( (lcd_active
? flite_power
: FLITE_PWR_OFF
),
89 (pwr
== FLITE_PWR_ON
&& lcd_active
? flite_brightness
: 0) );
92 void h3600_get_flite( struct h3600_ts_backlight
*bl
)
94 bl
->power
= flite_power
;
95 bl
->brightness
= flite_brightness
;
98 int h3600_set_contrast( unsigned char contrast
)
100 if (0) printk("%s: contrast=%d\n", __FUNCTION__
, contrast
);
102 screen_contrast
= contrast
;
103 return h3600_contrast_control( screen_contrast
);
106 void h3600_get_contrast( u_char
*contrast
)
108 *contrast
= screen_contrast
;
111 int h3600_toggle_frontlight( void )
113 return h3600_set_flite( 1 - flite_power
, flite_brightness
);
116 EXPORT_SYMBOL(h3600_get_flite
);
117 EXPORT_SYMBOL(h3600_set_flite
);
118 EXPORT_SYMBOL(h3600_get_contrast
);
119 EXPORT_SYMBOL(h3600_set_contrast
);
120 EXPORT_SYMBOL(h3600_toggle_frontlight
);
124 Check on blanking modes. Right now we seem to have problems with the console
125 blanking. Its recovery appears to be in interrupt context. Question - is this
126 function always called in interrupt context? If it is, move the h3600_flite_control
127 to a task. If not, should we be using the power manager?
129 This function is called by sa1100fb to turn on and off the backlight. There
130 are several reasons it can be called:
132 1. The framebuffer is being shut down from a suspend/resume.
133 2. Someone like an X server has requested blanking
135 On a blank request, we shut off the backlight. On an "unblank" request,
136 we restore the backlight to its prior setting.
139 static void h3600_hal_backlight_helper(int blank
)
141 if (0) printk(" %s: backlight=%d interrupt=%d\n", __FUNCTION__
, blank
, in_interrupt());
144 h3600_set_flite( flite_power
, flite_brightness
);
147 void h3600_hal_keypress( unsigned char key
)
149 if ( g_driver_ops
.keypress
)
150 g_driver_ops
.keypress(key
);
153 void h3600_hal_touchpanel( unsigned short x
, unsigned short y
, int down
)
155 if ( g_driver_ops
.touchpanel
)
156 g_driver_ops
.touchpanel(x
,y
,down
);
159 void h3600_hal_option_detect( int present
)
161 if ( g_driver_ops
.option_detect
)
162 g_driver_ops
.option_detect(present
);
165 EXPORT_SYMBOL(h3600_hal_keypress
);
166 EXPORT_SYMBOL(h3600_hal_touchpanel
);
167 EXPORT_SYMBOL(h3600_hal_option_detect
);
169 /***********************************************************************************/
170 /* Functions exported for use by the kernel and kernel modules */
171 /***********************************************************************************/
173 int h3600_apm_get_power_status(u_char
*ac_line_status
,
174 u_char
*battery_status
,
175 u_char
*battery_flag
,
176 u_char
*battery_percentage
,
177 u_short
*battery_life
)
179 struct h3600_battery bstat
;
180 unsigned char ac
= APM_AC_UNKNOWN
;
181 unsigned char level
= APM_BATTERY_STATUS_UNKNOWN
;
184 result
= h3600_get_battery(&bstat
);
186 printk("%s: unable to access battery information: result=%d\n", __FUNCTION__
, result
);
190 switch (bstat
.ac_status
) {
191 case H3600_AC_STATUS_AC_OFFLINE
:
194 case H3600_AC_STATUS_AC_ONLINE
:
197 case H3600_AC_STATUS_AC_BACKUP
:
202 if (ac_line_status
!= NULL
)
203 *ac_line_status
= ac
;
205 status
= bstat
.battery
[0].status
;
206 if (status
& (H3600_BATT_STATUS_CHARGING
| H3600_BATT_STATUS_CHARGE_MAIN
))
207 level
= APM_BATTERY_STATUS_CHARGING
;
208 else if (status
& (H3600_BATT_STATUS_HIGH
| H3600_BATT_STATUS_FULL
))
209 level
= APM_BATTERY_STATUS_HIGH
;
210 else if (status
& H3600_BATT_STATUS_LOW
)
211 level
= APM_BATTERY_STATUS_LOW
;
212 else if (status
& H3600_BATT_STATUS_CRITICAL
)
213 level
= APM_BATTERY_STATUS_CRITICAL
;
215 if (battery_status
!= NULL
)
216 *battery_status
= level
;
218 if (battery_percentage
!= NULL
)
219 *battery_percentage
= bstat
.battery
[0].percentage
;
221 /* assuming C/5 discharge rate */
222 if (battery_life
!= NULL
) {
223 *battery_life
= bstat
.battery
[0].life
;
224 *battery_life
|= 0x8000; /* Flag for minutes */
230 EXPORT_SYMBOL(h3600_apm_get_power_status
);
232 /***********************************************************************************/
233 /* Proc filesystem interface */
234 /***********************************************************************************/
236 static struct ctl_table h3600_hal_table
[] =
238 {3, "max_flite_brightness", &max_flite_brightness
, sizeof(max_flite_brightness
),
239 0666, NULL
, &proc_dointvec
},
243 static struct ctl_table h3600_hal_dir_table
[] =
245 {11, "hal", NULL
, 0, 0555, h3600_hal_table
},
249 static struct ctl_table_header
*h3600_hal_sysctl_header
= NULL
;
250 static struct proc_dir_entry
*hal_proc_dir
= NULL
;
252 enum hal_proc_index
{
263 struct h3600_proc_item
{
265 enum hal_proc_index id
;
268 static struct h3600_proc_item g_procitems
[] = {
269 {"version", HAL_VERSION
},
270 {"thermal", HAL_THERMAL_SENSOR
},
271 {"battery", HAL_BATTERY
},
272 {"light_sensor", HAL_LIGHT_SENSOR
},
273 {"assets", HAL_ASSETS
},
274 {"model", HAL_MODEL
},
275 {"screen_rotation", HAL_SCREEN_ROTATION
},
279 struct battery_flag_name
{
284 static struct battery_flag_name battery_chemistry
[] = {
285 { H3600_BATT_CHEM_ALKALINE
, "alkaline" },
286 { H3600_BATT_CHEM_NICD
, "NiCd" },
287 { H3600_BATT_CHEM_NIMH
, "NiMH" },
288 { H3600_BATT_CHEM_LION
, "Li-ion" },
289 { H3600_BATT_CHEM_LIPOLY
, "Li-Polymer" },
290 { H3600_BATT_CHEM_NOT_INSTALLED
, "Not installed" },
291 { H3600_BATT_CHEM_UNKNOWN
, "unknown" },
295 static struct battery_flag_name ac_status
[] = {
296 { H3600_AC_STATUS_AC_OFFLINE
, "offline" },
297 { H3600_AC_STATUS_AC_ONLINE
, "online" },
298 { H3600_AC_STATUS_AC_BACKUP
, "backup" },
299 { H3600_AC_STATUS_AC_UNKNOWN
, "unknown" },
303 static struct battery_flag_name battery_status
[] = {
304 { H3600_BATT_STATUS_HIGH
, "high" },
305 { H3600_BATT_STATUS_LOW
, "low" },
306 { H3600_BATT_STATUS_CRITICAL
, "critical" },
307 { H3600_BATT_STATUS_CHARGING
, "charging" },
308 { H3600_BATT_STATUS_CHARGE_MAIN
, "charge main" },
309 { H3600_BATT_STATUS_DEAD
, "dead" },
310 { H3600_BATT_STATUS_FULL
, "fully charged" },
311 { H3600_BATT_STATUS_NOBATT
, "no battery" },
315 static char * extract_flag_name( struct battery_flag_name
*list
, unsigned char value
)
317 while ( list
->bits
!= -1 && list
->bits
!= value
)
322 static void battery_status_to_string (unsigned char value
, char *buf
)
325 struct battery_flag_name
*f
= &battery_status
[0];
327 if (value
== H3600_BATT_STATUS_UNKNOWN
) {
328 strcpy (buf
, "unknown");
332 while (f
->bits
!= -1) {
333 if (value
& (f
->bits
)) {
339 p
+= strlen (f
->name
);
352 static struct params_list product_id
[] = {
357 static struct params_list page_mode
[] = {
363 /* These are guesses */
364 static struct params_list country_id
[] = {
371 { 0x8001, "Traditional Chinese" },
372 { 0x8002, "Simplified Chinese" },
373 { 0x8003, "Japanese" },
377 #define BASIC_FORMAT "%20s : "
379 static char * lookup_params( struct params_list
*list
, int value
)
384 while ( list
->value
!= -1 && list
->value
!= value
)
389 static char * asset_print_tchar( char *p
, struct h3600_asset
*asset
,
390 unsigned char *name
, struct params_list
*list
)
392 int len
= ASSET_TCHAR_LEN(asset
->type
);
393 unsigned char *data
= asset
->a
.tchar
;
395 // printk(" * Printing %s at %p, length = %d (%p)\n", name, data, len, p );
397 p
+= sprintf(p
, BASIC_FORMAT
, name
);
398 for ( ; len
> 0 ; len
-- ) {
407 static char * asset_print_word( char *p
, struct h3600_asset
*asset
,
408 unsigned char *name
, struct params_list
*list
)
410 unsigned short value
= asset
->a
.vshort
;
411 char *param
= lookup_params(list
,value
);
413 // printk(" * Printing %s value %d (%p)\n", name, value, p );
416 p
+= sprintf(p
, BASIC_FORMAT
"%d (%s)\n", name
, value
, param
);
418 p
+= sprintf(p
, BASIC_FORMAT
"%d\n", name
, value
);
423 static struct params_table params_table[] = {
424 { 0, PARAMS_TCHAR, 5, "HM Version" },
425 { 10, PARAMS_TCHAR, 20, "Serial #" },
426 { 50, PARAMS_TCHAR, 10, "Module ID" },
427 { 70, PARAMS_TCHAR, 5, "Product Revision" },
428 { 80, PARAMS_WORD, 0, "Product ID", product_id },
429 { 82, PARAMS_WORD, 0, "Frame Rate" },
430 { 84, PARAMS_WORD, 0, "Page Mode", page_mode },
431 { 86, PARAMS_WORD, 0, "Country ID", country_id },
432 { 88, PARAMS_WORD, 0, "Is Color Display" },
433 { 90, PARAMS_WORD, 0, "ROM Size" },
434 { 92, PARAMS_WORD, 0, "RAM Size" },
435 { 94, PARAMS_WORD, 0, "Horizontal pixels" },
436 { 96, PARAMS_WORD, 0, "Vertical pixels" },
437 { 0, PARAMS_DONE, 0, NULL }
441 //#define PRINT_TCHAR(_p,_name,_x) _p = print_param_tchar(_p, _name, _x, sizeof(_x))
443 typedef char * (*asset_printer
)( char *p
, struct h3600_asset
*asset
,
444 unsigned char * name
, struct params_list
*list
);
446 static asset_printer asset_printers
[] = {
451 struct params_table
{
454 struct params_list
*list
;
457 static struct params_table params_table
[] = {
458 { ASSET_HM_VERSION
, "HM Version" },
459 { ASSET_SERIAL_NUMBER
, "Serial #" },
460 { ASSET_MODULE_ID
, "Module ID" },
461 { ASSET_PRODUCT_REVISION
, "Product Revision" },
462 { ASSET_PRODUCT_ID
, "Product ID", product_id
},
463 { ASSET_FRAME_RATE
, "Frame Rate" },
464 { ASSET_PAGE_MODE
, "Page Mode", page_mode
},
465 { ASSET_COUNTRY_ID
, "Country ID", country_id
},
466 { ASSET_IS_COLOR_DISPLAY
, "Is Color Display" },
467 { ASSET_ROM_SIZE
, "ROM Size" },
468 { ASSET_RAM_SIZE
, "RAM Size" },
469 { ASSET_HORIZONTAL_PIXELS
,"Horizontal pixels" },
470 { ASSET_VERTICAL_PIXELS
, "Vertical pixels" }
473 static char * h3600_hal_parse_eeprom( char *p
)
475 struct h3600_asset asset
;
479 // printk( __FUNCTION__ " : called with %p\n", p );
481 /* Suck in the data */
482 // retval = h3600_asset_read( &asset );
484 // p += sprintf(p,"Error value %d\n", retval);
488 // retval = sizeof(asset.hm_version);
489 // p = print_param_tchar( p, "HM Version", asset.hm_version, retval );
490 // printk(" *** assets at %p\n", &asset );
492 for ( i
= 0 ; i
< (sizeof(params_table
) / sizeof(struct params_table
)) ; i
++ ) {
493 // printk( " ** parsing %d 0x%08lX %ld\n", i, params_table[i].asset,
494 // ASSET_TYPE(params_table[i].asset));
495 asset
.type
= params_table
[i
].asset
;
496 retval
= h3600_asset_read( &asset
);
498 p
+= sprintf(p
,"Error value %d\n", retval
);
501 p
= asset_printers
[ASSET_TYPE(params_table
[i
].asset
)]( p
, &asset
,
502 params_table
[i
].name
,
503 params_table
[i
].list
);
505 /* PRINT_TCHAR( p, "HM Version", asset.hm_version );
506 PRINT_TCHAR( p, "Serial #", asset.serial_number );
507 PRINT_TCHAR( p, "Module ID", asset.module_id );
508 PRINT_TCHAR( p, "Product Revision", asset.product_revision );
510 p = print_param_word( p, "Product ID", asset.product_id, product_id );
511 p = print_param_word( p, "Frame Rate", asset.frame_rate, NULL );
512 p = print_param_word( p, "Page Mode", asset.page_mode, page_mode );
513 p = print_param_word( p, "Country ID", asset.country_id, country_id );
514 p = print_param_word( p, "Is Color Display", asset.is_color_display, NULL );
515 p = print_param_word( p, "ROM Size", asset.rom_size, NULL );
516 p = print_param_word( p, "RAM Size", asset.ram_size, NULL );
517 p = print_param_word( p, "Horizontal pixels", asset.horizontal_pixels, NULL );
518 p = print_param_word( p, "Vertical pixels", asset.vertical_pixels, NULL );*/
520 // printk( __FUNCTION__ " : returning %p\n", p );
525 #include <linux/ctype.h>
526 #define DUMP_BUF_SIZE 16
527 #define MYPRINTABLE(x) \
528 ( ((x)<='z' && (x)>='a') || ((x)<='Z' && (x)>='A') || isdigit(x) || (x)==':' )
530 static void dump_mem( unsigned char *p, int count )
532 u_char pbuf[DUMP_BUF_SIZE + 1];
536 printk(__FUNCTION__ " %p %d\n", p, count);
540 if ( len > DUMP_BUF_SIZE )
543 for ( i = 0 ; i < len ; i++ ) {
544 printk(" %02x", *p );
545 pbuf[i] = ( MYPRINTABLE(*p) ? *p : '.' );
551 printk(" %s\n", pbuf);
556 static int h3600_hal_proc_item_read(char *page
, char **start
, off_t off
,
557 int count
, int *eof
, void *data
)
563 if (0) printk("%s: %p %p %ld %d %p %p\n", __FUNCTION__
, page
, start
, off
, count
, eof
, data
);
568 struct h3600_ts_version v
;
569 retval
= h3600_get_version( &v
);
571 p
+= sprintf(p
, "Host : %s\n", v
.host_version
);
572 p
+= sprintf(p
, "Pack : %s\n", v
.pack_version
);
573 p
+= sprintf(p
, "Boot type : 0x%02x\n", v
.boot_type
);
577 case HAL_THERMAL_SENSOR
:
580 retval
= h3600_get_thermal_sensor( &v
);
582 p
+= sprintf(p
, "0x%04x\n", v
);
587 struct h3600_battery v
;
588 retval
= h3600_get_battery( &v
);
592 p
+= sprintf(p
, "AC status : %x (%s)\n", v
.ac_status
,
593 extract_flag_name(ac_status
,v
.ac_status
));
595 for ( i
= 0 ; i
< v
.battery_count
; i
++ ) {
596 p
+= sprintf(p
, "Battery #%d\n", i
);
597 p
+= sprintf(p
, " Chemistry : 0x%02x (%s)\n",
598 v
.battery
[i
].chemistry
,
599 extract_flag_name(battery_chemistry
,
600 v
.battery
[i
].chemistry
));
601 battery_status_to_string (v
.battery
[i
].status
, buf
);
602 p
+= sprintf(p
, " Status : 0x%02x (%s)\n",
606 p
+= sprintf(p
, " Voltage : 0x%04x (%4d mV)\n",
607 v
.battery
[i
].voltage
,
608 v
.battery
[i
].voltage
* 5000 / 1024);
609 p
+= sprintf(p
, " Percentage : 0x%02x", v
.battery
[i
].percentage
);
610 if ( v
.battery
[i
].percentage
== 0xff )
611 p
+= sprintf(p
, " (unknown)\n");
613 p
+= sprintf(p
, " (%d%%)\n", v
.battery
[i
].percentage
);
615 p
+= sprintf(p
, " Life (min) : %d\n", v
.battery
[i
].life
);
620 case HAL_LIGHT_SENSOR
:
623 retval
= h3600_get_light_sensor(&level
);
625 p
+= sprintf(p
,"0x%02x\n", level
);
629 p
= h3600_hal_parse_eeprom( p
);
632 p
+= sprintf(p
,"%s\n", h3600_generic_name() );
634 case HAL_SCREEN_ROTATION
:
635 p
+= sprintf(p
,"%s\n", h3600_screen_rotation() );
638 p
+= sprintf(p
,"Unsupported item %d\n", (int)data
);
643 p
+= sprintf(p
,"Error value %d\n", retval
);
646 // dump_mem( page, p - page );
647 len
= (p
- page
) - off
;
651 *eof
= (len
<= count
) ? 1 : 0;
658 /***********************************************************************************/
659 /* Registration functions from low level and from device drivers */
660 /***********************************************************************************/
662 int h3600_hal_register_interface( struct h3600_hal_ops
*ops
)
664 if ( !ops
|| !ops
->owner
)
668 /* Initialize our screen and frontlight */
669 h3600_set_flite( flite_power
, flite_brightness
);
670 h3600_set_contrast( screen_contrast
);
675 void h3600_hal_unregister_interface( struct h3600_hal_ops
*ops
)
677 h3600_hal_ops
= NULL
;
680 EXPORT_SYMBOL(h3600_hal_register_interface
);
681 EXPORT_SYMBOL(h3600_hal_unregister_interface
);
684 if ( ops->x ) g_driver_ops.x = ops->x
686 #define HAL_UNREG(x) \
687 if ( ops->x ) g_driver_ops.x = NULL
689 int h3600_hal_register_driver( struct h3600_driver_ops
*ops
)
693 HAL_REG(option_detect
);
697 void h3600_hal_unregister_driver( struct h3600_driver_ops
*ops
)
700 HAL_UNREG(touchpanel
);
701 HAL_UNREG(option_detect
);
704 EXPORT_SYMBOL(h3600_hal_register_driver
);
705 EXPORT_SYMBOL(h3600_hal_unregister_driver
);
708 /***********************************************************************************/
710 /***********************************************************************************/
712 #ifdef CONFIG_FB_SA1100
713 extern void (*sa1100fb_blank_helper
)(int blank
);
716 extern void (*pxafb_blank_helper
)(int blank
);
719 int __init
h3600_hal_init_module(void)
722 printk(KERN_INFO
"H3600 Registering HAL abstraction layer\n");
724 /* Request the appropriate underlying module to provide services */
725 /* TODO: We've put this here to avoid having to add another /etc/module
726 line for people upgrading their kernel. In the future, we'd like to
727 automatically request and load the appropriate subsystem based on the
728 type of iPAQ owned */
729 if ( machine_is_h3100() || machine_is_h3600() ) {
730 request_module("h3600_micro");
732 else if ( machine_is_h3800() ) {
733 request_module("h3600_asic");
735 else if ( machine_is_h3900() ) {
736 request_module("h3900_asic");
737 } else if ( machine_is_h5400() ) {
738 request_module("h5400_asic");
739 } else if ( machine_is_h1900() ) {
740 request_module("h1900_asic");
743 h3600_hal_sysctl_header
= register_sysctl_table(h3600_hal_dir_table
, 0);
744 #ifdef CONFIG_FB_SA1100
745 sa1100fb_blank_helper
= h3600_hal_backlight_helper
;
748 pxafb_blank_helper
= h3600_hal_backlight_helper
;
751 /* Register in /proc filesystem */
752 hal_proc_dir
= proc_mkdir(H3600_HAL_PROC_DIR
, NULL
);
754 for ( i
= 0 ; g_procitems
[i
].id
!= HAL_DONE
; i
++ )
755 create_proc_read_entry(g_procitems
[i
].name
, 0, hal_proc_dir
,
756 h3600_hal_proc_item_read
, (void *) g_procitems
[i
].id
);
758 printk(KERN_ALERT
"%s: unable to create proc entry %s\n", __FUNCTION__
, H3600_HAL_PROC_DIR
);
762 void h3600_hal_cleanup_module(void)
765 printk(KERN_INFO
"H3600 shutting down HAL abstraction layer\n");
767 #ifdef CONFIG_FB_SA1100
768 sa1100fb_blank_helper
= NULL
;
771 pxafb_blank_helper
= NULL
;
773 unregister_sysctl_table(h3600_hal_sysctl_header
);
775 if ( hal_proc_dir
) {
776 for ( i
= 0 ; g_procitems
[i
].id
!= HAL_DONE
; i
++ )
777 remove_proc_entry(g_procitems
[i
].name
, hal_proc_dir
);
778 remove_proc_entry(H3600_HAL_PROC_DIR
, NULL
);
783 module_init(h3600_hal_init_module
);
784 module_exit(h3600_hal_cleanup_module
);