code:
/*	$OpenBSD: hilkbdmap.c,v 1.7 2005/05/07 22:42:30 miod Exp $	*/
/*
 * Copyright (c) 2003, Miodrag Vallat.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <sys/types.h>
#include <dev/wscons/wsksymdef.h>
#include <dev/wscons/wsksymvar.h>
#include <dev/hil/hilkbdmap.h>

#ifdef WSDISPLAY_COMPAT_RAWKBD
#include <dev/wscons/wskbdraw.h>

/*
 * Translate HIL keycodes to US keyboard XT scancodes, for proper
 * X11-over-wsmux operation.
 */
const u_int8_t hilkbd_raw[0x80] = {
	RAWKEY_Control_R,
	RAWKEY_Null,
	RAWKEY_Alt_R,
	RAWKEY_Alt_L,
	RAWKEY_Shift_R,
	RAWKEY_Shift_L,
	RAWKEY_Control_L,
	RAWKEY_Pause,		/* 7 Break/Reset */
	RAWKEY_KP_Left,
	RAWKEY_KP_Up,
	RAWKEY_KP_Begin,
	RAWKEY_KP_Prior,
	RAWKEY_KP_Right,
	RAWKEY_KP_Home,
	RAWKEY_KP_Delete,
	RAWKEY_KP_Enter,
	RAWKEY_KP_End,
	RAWKEY_KP_Divide,
	RAWKEY_KP_Down,
	RAWKEY_KP_Add,
	RAWKEY_KP_Next,
	RAWKEY_KP_Multiply,
	RAWKEY_KP_Insert,
	RAWKEY_KP_Subtract,
	RAWKEY_b,
	RAWKEY_v,
	RAWKEY_c,
	RAWKEY_x,
	RAWKEY_z,
	RAWKEY_Null,		/* 29 Kanji Left */
	RAWKEY_Null,		/* 30 */
	RAWKEY_Escape,
	RAWKEY_Null,
	RAWKEY_f10,
	RAWKEY_Null,
	RAWKEY_f11,
	RAWKEY_KP_Delete,
	RAWKEY_f9,
	RAWKEY_Tab,
	RAWKEY_f12,
	RAWKEY_h,
	RAWKEY_g,
	RAWKEY_f,
	RAWKEY_d,
	RAWKEY_s,
	RAWKEY_a,
	RAWKEY_Null,		/* 46 */
	RAWKEY_Caps_Lock,
	RAWKEY_u,
	RAWKEY_y,
	RAWKEY_t,
	RAWKEY_r,
	RAWKEY_e,
	RAWKEY_w,
	RAWKEY_q,
	RAWKEY_Tab,
	RAWKEY_7,
	RAWKEY_6,
	RAWKEY_5,
	RAWKEY_4,
	RAWKEY_3,
	RAWKEY_2,
	RAWKEY_1,
	RAWKEY_grave,
	RAWKEY_Null,		/* 64 */
	RAWKEY_Null,		/* 65 */
	RAWKEY_Null,		/* 66 */
	RAWKEY_Null,		/* 67 */
	RAWKEY_Null,		/* 68 */
	RAWKEY_Null,		/* 69 */
	RAWKEY_Null,		/* 70 */
	RAWKEY_Null,		/* 71 */
	RAWKEY_Print_Screen,
	RAWKEY_f4,
	RAWKEY_f3,
	RAWKEY_f2,
	RAWKEY_f1,
	RAWKEY_Null,		/* 77 */
	RAWKEY_Hold_Screen,
	RAWKEY_Return,
	RAWKEY_Num_Lock,
	RAWKEY_f5,
	RAWKEY_f6,
	RAWKEY_f7,
	RAWKEY_f8,
	RAWKEY_Null,		/* 85 */
	RAWKEY_Null,		/* 86 Clear line */
	RAWKEY_Null,		/* 87 Clear display */
	RAWKEY_8,
	RAWKEY_9,
	RAWKEY_0,
	RAWKEY_minus,
	RAWKEY_equal,
	RAWKEY_BackSpace,
	RAWKEY_Null,		/* 94 Insert line */
	RAWKEY_Null,		/* 95 Delete line */
	RAWKEY_i,
	RAWKEY_o,
	RAWKEY_p,
	RAWKEY_bracketleft,
	RAWKEY_bracketright,
	RAWKEY_backslash,
	RAWKEY_Insert,
	RAWKEY_Delete,
	RAWKEY_j,
	RAWKEY_k,
	RAWKEY_l,
	RAWKEY_semicolon,
	RAWKEY_apostrophe,
	RAWKEY_Return,
	RAWKEY_Home,
	RAWKEY_Prior,
	RAWKEY_m,
	RAWKEY_comma,
	RAWKEY_period,
	RAWKEY_slash,
	RAWKEY_Null,		/* 116 */
	RAWKEY_End,
	RAWKEY_Null,		/* 118 */
	RAWKEY_Next,
	RAWKEY_n,
	RAWKEY_space,
	RAWKEY_Null,		/* 122 */
	RAWKEY_Null,		/* 123 Kanji Right */
	RAWKEY_Left,
	RAWKEY_Down,
	RAWKEY_Up,
	RAWKEY_Right
};
#endif

#define KC(n) KS_KEYCODE(n)

/*
 * 1f. US ASCII
 *
 * We use the same table for PS/2 and old HIL keyboards, as the only
 * differences are a few keys which are only present in one of both layouts,
 * and the one-function-only keypad in the old HIL flavour (hilkbd.c knows
 * about this and does The Right Thing).
 */

static const keysym_t hilkbd_keydesc_us[] = {
/*  pos      command		normal		shifted */
    KC(0),   KS_Cmd1,		KS_Control_R,
    KC(2),   KS_Cmd2,		KS_Mode_switch,	KS_Multi_key,
    KC(3),   KS_Cmd2,		KS_Alt_L,
    KC(4),			KS_Shift_R,
    KC(5),			KS_Shift_L,
    KC(6),   KS_Cmd1,		KS_Control_L,
    KC(7),   KS_Cmd_KbdReset,			/* Break/Reset */
    KC(8),			KS_KP_Left,	KS_KP_4,
    KC(9),			KS_KP_Up,	KS_KP_8,
    KC(10),			KS_KP_Begin,	KS_KP_5,
    KC(11),			KS_KP_Prior,	KS_KP_9,
    KC(12),			KS_KP_Right,	KS_KP_6,
    KC(13),			KS_KP_Home,	KS_KP_7,
    KC(14),			KS_KP_Separator,
    KC(15),			KS_KP_Enter,
    KC(16),			KS_KP_End,	KS_KP_1,
    KC(17),			KS_KP_Divide,
    KC(18),			KS_KP_Down,	KS_KP_2,
    KC(19),			KS_KP_Add,
    KC(20),			KS_KP_Next,	KS_KP_3,
    KC(21),			KS_KP_Multiply,
    KC(22),			KS_KP_Insert,	KS_KP_0,
    KC(23),			KS_KP_Subtract,
    KC(24),			KS_b,
    KC(25),			KS_v,		KS_V,		KS_section,
    KC(26),			KS_c,		KS_C,		KS_ccedilla,
    KC(27),			KS_x,		KS_X,		KS_L2_scaron,
    KC(28),			KS_z,		KS_Z,		KS_paragraph,
    /* 29 Kanji Left */

    KC(31), KS_Cmd_Debugger,	KS_Escape,	KS_Delete,
    KC(33), KS_Cmd_Screen9,	KS_f10,		/* also KS_KP_F2 */
    KC(35), KS_Cmd_Screen10,	KS_f11,		/* also KS_KP_F3 */
    KC(36),			KS_KP_Delete,	KS_KP_Decimal,
    KC(37), KS_Cmd_Screen8,	KS_f9,		/* also KS_KP_F1 */
    KC(38),			KS_Tab,		/* numeric pad */
    KC(39), KS_Cmd_Screen11,	KS_f12,		/* also KS_KP_F4 */
    KC(40),			KS_h,		KS_H,		KS_yen,
    KC(41),			KS_g,		KS_G,		KS_currency,
    KC(42),			KS_f,
    KC(43),			KS_d,		KS_D,		KS_eth,
    KC(44),			KS_s,		KS_S,		KS_ssharp,
    KC(45),			KS_a,		KS_A,		KS_aring,
    /* 46 Mode_Switch ??? */
    KC(47),			KS_Caps_Lock,
    KC(48),			KS_u,		KS_U,		KS_dead_diaeresis,
    KC(49),			KS_y,		KS_Y,		KS_dead_circumflex,
    KC(50),			KS_t,		KS_T,		KS_dead_grave,
    KC(51),			KS_r,		KS_R,		KS_dead_acute,
    KC(52),			KS_e,		KS_E,		KS_ae,
    KC(53),			KS_w,		KS_W,		KS_asciitilde,
    KC(54),			KS_q,		KS_Q,		KS_periodcentered,
    KC(55),			KS_Tab,
    KC(56),			KS_7,		KS_ampersand,	KS_backslash,
    KC(57),			KS_6,		KS_asciicircum,	KS_asciicircum,
    KC(58),			KS_5,		KS_percent,	KS_onehalf,
    KC(59),			KS_4,		KS_dollar,
				KS_onequarter,	KS_threequarters,
    KC(60),			KS_3,		KS_numbersign,	KS_numbersign,
    KC(61),			KS_2,		KS_at,		KS_at,
    KC(62),			KS_1,		KS_exclam,	KS_exclamdown,
    KC(63),			KS_grave,	KS_asciitilde,
				KS_guillemotleft,KS_guillemotright,

    KC(72),			KS_Print_Screen, /* Menu */
    KC(73),  KS_Cmd_Screen3,	KS_f4,
    KC(74),  KS_Cmd_Screen2,	KS_f3,
    KC(75),  KS_Cmd_Screen1,	KS_f2,
    KC(76),  KS_Cmd_Screen0,	KS_f1,

    KC(78),			KS_Hold_Screen,
    KC(79),			KS_Return,	KS_Print_Screen,
    KC(80),			KS_Num_Lock,	/* System/User */
    KC(81),  KS_Cmd_Screen4,	KS_f5,
    KC(82),  KS_Cmd_Screen5,	KS_f6,
    KC(83),  KS_Cmd_Screen6,	KS_f7,
    KC(84),  KS_Cmd_Screen7,	KS_f8,

    /* 86 Clear line */
    KC(87),			KS_Clear,
    KC(88),			KS_8,		KS_asterisk,
				KS_bracketleft,	KS_braceleft,
    KC(89),			KS_9,		KS_parenleft,
				KS_bracketright,KS_braceright,
    KC(90),			KS_0,		KS_parenright,	KS_questiondown,
    KC(91),			KS_minus,	KS_underscore,	KS_macron,
    KC(92),			KS_equal,	KS_plus,	KS_plusminus,
    KC(93),  KS_Cmd_ResetEmul,	KS_Delete,	/* Backspace */
    /* 94 Insert line */
    /* 95 Delete line */
    KC(96),			KS_i,		KS_I,		KS_dead_tilde,
    KC(97),			KS_o,		KS_O,		KS_oslash,
    KC(98),			KS_p,		KS_P,		KS_thorn,
    KC(99),			KS_bracketleft,	KS_braceleft,	KS_degree,
    KC(100),			KS_bracketright,KS_braceright,
				KS_bar,		KS_brokenbar,
    KC(101),			KS_backslash,	KS_bar,		KS_mu,
    KC(102),			KS_Insert,
    KC(103),			KS_Delete,
    KC(104),			KS_j,		KS_J,		KS_dollar,
    KC(105),			KS_k,		KS_K,		KS_cent,
    KC(106),			KS_l,		KS_L,		KS_sterling,
    KC(107),			KS_semicolon,	KS_colon,
    KC(108),			KS_apostrophe,	KS_quotedbl,
				KS_grave,	KS_apostrophe,
    KC(109),			KS_Return,
    KC(110),			KS_Home,
    KC(111), KS_Cmd_ScrollBack,	KS_Prior,
    KC(112),			KS_m,		KS_M,		KS_masculine,
    KC(113),			KS_comma,	KS_less,	KS_less,
    KC(114),			KS_period,	KS_greater,	KS_greater,
    KC(115),			KS_slash,	KS_question,	KS_underscore,

    KC(117),			KS_End,		/* Select */

    KC(119), KS_Cmd_ScrollFwd,	KS_Next,
    KC(120),			KS_n,		KS_N,		KS_ordfeminine,
    KC(121),			KS_space,

    /* 123 Kanji Right */
    KC(124),			KS_Left,
    KC(125),			KS_Down,
    KC(126),			KS_Up,
    KC(127),			KS_Right,
};

/*
 * 0e. Swedish
 */

static const keysym_t hilkbd_keydesc_sv[] = {
    KC(56),	KS_7,		KS_slash,	KS_backslash,
    KC(57),	KS_6,		KS_ampersand,	KS_asciicircum,
    KC(61),	KS_2,		KS_quotedbl,	KS_at,
    KC(63),	KS_less,	KS_greater,
		KS_guillemotleft,KS_guillemotright,
    KC(88),	KS_8,		KS_parenleft,	KS_bracketleft,	KS_braceleft,
    KC(89),	KS_9,		KS_parenright,	KS_bracketright,KS_braceright,
    KC(90),	KS_0,		KS_equal,	KS_exclamdown,
    KC(91),	KS_plus,	KS_question,	KS_macron,
    KC(92),	KS_eacute,	KS_Eacute,	KS_plusminus,
    KC(97),	KS_aring,	KS_Aring,	KS_degree,
    KC(98),	KS_udiaeresis,	KS_Udiaeresis,	KS_bar,		KS_brokenbar,
    KC(99),	KS_apostrophe,	KS_asterisk,	KS_mu,
    KC(107),	KS_odiaeresis,
    KC(108),	KS_adiaeresis,	KS_Adiaeresis,	KS_grave,	KS_apostrophe,
    KC(113),	KS_comma,	KS_semicolon,	KS_less,
    KC(114),	KS_period,	KS_colon,	KS_greater,
    KC(115),	KS_minus,	KS_underscore,	KS_underscore
};

static const keysym_t hilkbd_keydesc_sv_nodead[] = {
    KC(48),	KS_u,		KS_U,		KS_diaeresis,
    KC(49),	KS_y,		KS_Y,		KS_asciicircum,
    KC(50),	KS_t,		KS_T,		KS_grave,
    KC(51),	KS_r,		KS_R,		KS_apostrophe,
    KC(96),	KS_i,		KS_I,		KS_asciitilde,
};

static const keysym_t hilkbd_keydesc_sv_ps2[] = {
    KC(24),	KS_b,
    KC(25),	KS_v,
    KC(26),	KS_c,
    KC(27),	KS_x,
    KC(28),	KS_z,
    KC(36),	KS_KP_Separator,KS_KP_Delete,
    KC(40),	KS_h,
    KC(41),	KS_g,
    KC(42),	KS_f,
    KC(43),	KS_d,
    KC(44),	KS_s,
    KC(45),	KS_a,
    KC(48),	KS_u,
    KC(49),	KS_y,
    KC(50),	KS_t,
    KC(51),	KS_r,
    KC(52),	KS_e,
    KC(53),	KS_w,
    KC(54),	KS_q,
    KC(56),	KS_7,		KS_slash,	KS_braceleft,
    KC(57),	KS_6,		KS_ampersand,
    KC(58),	KS_5,		KS_percent,
    KC(59),	KS_4,		KS_currency,	KS_dollar,
    KC(60),	KS_3,		KS_numbersign,	KS_sterling,
    KC(61),	KS_2,		KS_quotedbl,	KS_at,
    KC(62),	KS_1,		KS_exclam,
    KC(63),	KS_section,	KS_onehalf,
    KC(88),	KS_8,		KS_parenleft,	KS_bracketleft,
    KC(89),	KS_9,		KS_parenright,	KS_bracketright,
    KC(90),	KS_0,		KS_equal,	KS_braceright,
    KC(91),	KS_minus,	KS_question,	KS_backslash,
    KC(92),	KS_dead_acute,	KS_dead_grave,
    KC(96),	KS_i,
    KC(97),	KS_o,
    KC(98),	KS_p,
    KC(99),	KS_aring,
    KC(100),	KS_dead_diaeresis,KS_dead_circumflex,KS_dead_tilde,
    KC(104),	KS_j,
    KC(105),	KS_k,
    KC(106),	KS_l,
    KC(107),	KS_odiaeresis,
    KC(108),	KS_adiaeresis,
    KC(112),	KS_m,
    KC(113),	KS_comma,	KS_semicolon,
    KC(114),	KS_period,	KS_colon,
    KC(115),	KS_minus,	KS_underscore,
    KC(116),	KS_apostrophe,	KS_asterisk,
    KC(118),	KS_less,	KS_greater,	KS_bar,
    KC(120),	KS_n
};

static const keysym_t hilkbd_keydesc_sv_ps2_nodead[] = {
    KC(92),	KS_apostrophe,	KS_grave,
    KC(100),	KS_diaeresis,	KS_asciicircum,	KS_asciitilde,
};

/*
 * 0f. German
 */

static const keysym_t hilkbd_keydesc_de[] = {
    KC(28),	KS_y,		KS_Y,		KS_paragraph,
    KC(49),	KS_z,		KS_Z,		KS_dead_circumflex,
    KC(56),	KS_7,		KS_slash,	KS_backslash,
    KC(57),	KS_6,		KS_ampersand,	KS_asciicircum,
    KC(60),	KS_3,		KS_section,	KS_numbersign,
    KC(61),	KS_2,		KS_quotedbl,	KS_at,
    KC(63),	KS_less,	KS_greater,
		KS_guillemotleft,KS_guillemotright,
    KC(88),	KS_8,		KS_parenleft,
		KS_bracketleft,	KS_braceleft,
    KC(89),	KS_9,		KS_parenright,
		KS_bracketright,KS_braceright,
    KC(90),	KS_0,		KS_equal,	KS_exclamdown,
    KC(91),	KS_ssharp,	KS_question,	KS_macron,
    KC(92),	KS_dead_acute,	KS_dead_grave,	KS_plusminus,
    KC(99),	KS_udiaeresis,	KS_Udiaeresis,	KS_dead_abovering,
    KC(100),	KS_plus,	KS_asterisk,	KS_bar,		KS_brokenbar,
    KC(101),	KS_sterling,	KS_dead_circumflex,KS_mu,
    KC(107),	KS_odiaeresis,
    KC(108),	KS_adiaeresis,	KS_Adiaeresis,	KS_grave,	KS_apostrophe,
    KC(113),	KS_comma,	KS_semicolon,	KS_less,
    KC(114),	KS_period,	KS_colon,	KS_greater,
    KC(115),	KS_minus,	KS_underscore,	KS_underscore
};

static const keysym_t hilkbd_keydesc_de_nodead[] = {
    KC(48),	KS_u,		KS_U,		KS_diaeresis,
    KC(49),	KS_z,		KS_Z,		KS_asciicircum,
    KC(50),	KS_t,		KS_T,		KS_grave,
    KC(51),	KS_r,		KS_R,		KS_apostrophe,
    KC(92),	KS_apostrophe,	KS_grave,	KS_plusminus,
    KC(99),	KS_udiaeresis,	KS_Udiaeresis,	KS_degree,
    KC(101),	KS_sterling,	KS_asciicircum,	KS_mu,
    KC(96),	KS_i,		KS_I,		KS_asciitilde
};

static const keysym_t hilkbd_keydesc_de_ps2[] = {
    KC(24),	KS_b,
    KC(25),	KS_v,
    KC(26),	KS_c,
    KC(27),	KS_x,
    KC(28),	KS_y,
    KC(36),	KS_KP_Separator,KS_KP_Delete,
    KC(40),	KS_h,
    KC(41),	KS_g,
    KC(42),	KS_f,
    KC(43),	KS_d,
    KC(44),	KS_s,
    KC(45),	KS_a,
    KC(48),	KS_u,
    KC(49),	KS_z,		KS_Z,		KS_dead_circumflex,
    KC(50),	KS_t,
    KC(51),	KS_r,
    KC(52),	KS_e,
    KC(53),	KS_w,
    KC(54),	KS_q,		KS_Q,		KS_at,
    KC(56),	KS_7,		KS_slash,	KS_braceleft,
    KC(57),	KS_6,		KS_ampersand,
    KC(58),	KS_5,		KS_percent,
    KC(59),	KS_4,		KS_dollar,
    KC(60),	KS_3,		KS_section,	KS_threesuperior,
    KC(61),	KS_2,		KS_quotedbl,	KS_twosuperior,
    KC(62),	KS_1,		KS_exclam,
    KC(63),	KS_dead_circumflex,	KS_dead_abovering,
    KC(88),	KS_8,		KS_parenleft,	KS_bracketleft,
    KC(89),	KS_9,		KS_parenright,	KS_bracketright,
    KC(90),	KS_0,		KS_equal,	KS_braceright,
    KC(91),	KS_ssharp,	KS_question,	KS_backslash,
    KC(92),	KS_dead_acute,	KS_dead_grave,
    KC(96),	KS_i,
    KC(97),	KS_o,
    KC(98),	KS_p,
    KC(99),	KS_udiaeresis,
    KC(100),	KS_plus,	KS_asterisk,	KS_dead_tilde,
    KC(104),	KS_j,
    KC(105),	KS_k,
    KC(106),	KS_l,
    KC(107),	KS_odiaeresis,
    KC(108),	KS_adiaeresis,
    KC(112),	KS_m,		KS_M,		KS_mu,
    KC(113),	KS_comma,	KS_semicolon,
    KC(114),	KS_period,	KS_colon,
    KC(115),	KS_minus,	KS_underscore,
    KC(116),	KS_numbersign,	KS_apostrophe,
    KC(118),	KS_less,	KS_greater, 	KS_bar,
    KC(120),	KS_n
};

static const keysym_t hilkbd_keydesc_de_ps2_nodead[] = {
    KC(49),	KS_z,		KS_Z,		KS_asciicircum,
    KC(63),	KS_asciicircum,	KS_degree,
    KC(92),	KS_apostrophe,	KS_grave,
    KC(100),	KS_plus,	KS_asterisk,	KS_asciitilde
};

/*
 * 17. English
 */

static const keysym_t hilkbd_keydesc_uk[] = {
    KC(56),	KS_7,		KS_asciicircum,	KS_backslash,
    KC(57),	KS_6,		KS_ampersand,	KS_asciicircum,
    KC(60),	KS_3,		KS_sterling,	KS_numbersign,
    KC(61),	KS_2,		KS_quotedbl,	KS_at,
    KC(88),	KS_8,		KS_parenleft,	KS_bracketleft,	KS_braceleft,
    KC(89),	KS_9,		KS_parenright,	KS_bracketright,KS_braceright,
    KC(90),	KS_0,		KS_equal,	KS_questiondown,
    KC(91),	KS_plus,	KS_question,	KS_macron,
    KC(92),	KS_apostrophe,	KS_slash,	KS_plusminus,
    KC(101),	KS_less,	KS_greater,	KS_mu,
    KC(107),	KS_asterisk,	KS_at,
    KC(108),	KS_backslash,	KS_bar,		KS_grave,	KS_apostrophe,
    KC(113),	KS_comma,	KS_semicolon,	KS_less,
    KC(114),	KS_period,	KS_colon,	KS_greater,
    KC(115),	KS_minus,	KS_underscore,	KS_underscore
};

static const keysym_t hilkbd_keydesc_uk_ps2[] = {
    KC(24),	KS_b,
    KC(25),	KS_v,
    KC(26),	KS_c,
    KC(27),	KS_x,
    KC(28),	KS_z,
    KC(40),	KS_h,
    KC(41),	KS_g,
    KC(42),	KS_f,
    KC(43),	KS_d,
    KC(44),	KS_s,
    KC(45),	KS_a,
    KC(48),	KS_u,
    KC(49),	KS_y,
    KC(50),	KS_t,
    KC(51),	KS_r,
    KC(52),	KS_e,
    KC(53),	KS_w,
    KC(54),	KS_q,
    KC(56),	KS_7,		KS_ampersand,
    KC(57),	KS_6,		KS_asciicircum,
    KC(58),	KS_5,		KS_percent,
    KC(59),	KS_4,		KS_dollar,
    KC(60),	KS_3,		KS_sterling,
    KC(61),	KS_2,		KS_quotedbl,
    KC(62),	KS_1,		KS_exclam,
    KC(63),	KS_grave,	KS_notsign,	KS_bar,
    KC(88),	KS_8,		KS_asterisk,
    KC(89),	KS_9,		KS_parenleft,
    KC(90),	KS_0,		KS_parenright,
    KC(91),	KS_minus,	KS_underscore,
    KC(92),	KS_equal,	KS_plus,
    KC(96),	KS_i,
    KC(97),	KS_o,
    KC(98),	KS_p,
    KC(99),	KS_bracketleft,	KS_braceleft,
    KC(100),	KS_bracketright,KS_braceright,
    KC(104),	KS_j,
    KC(105),	KS_k,
    KC(106),	KS_l,
    KC(107),	KS_semicolon,	KS_colon,
    KC(108),	KS_apostrophe,	KS_at,
    KC(112),	KS_m,
    KC(113),	KS_comma,	KS_less,
    KC(114),	KS_period,	KS_greater,
    KC(115),	KS_slash,	KS_question,
    KC(116),	KS_numbersign,	KS_asciitilde,
    KC(118),	KS_backslash,	KS_brokenbar,
    KC(120),	KS_n
};

/*
 * 1b. French
 */

static const keysym_t hilkbd_keydesc_fr[] = {
    KC(28),	KS_w,		KS_W,		KS_paragraph,
    KC(45),	KS_q,		KS_Q,		KS_aring,
    KC(53),	KS_z,		KS_Z,		KS_asciitilde,
    KC(54),	KS_a,		KS_A,		KS_periodcentered,
    KC(56),	KS_egrave,	KS_7,		KS_backslash,
    KC(57),	KS_section,	KS_6,		KS_asciicircum,
    KC(58),	KS_parenleft,	KS_5,		KS_onehalf,
    KC(59),	KS_apostrophe,	KS_4,		KS_onequarter,	KS_threequarters,
    KC(60),	KS_quotedbl,	KS_3,		KS_numbersign,
    KC(61),	KS_eacute,	KS_2,		KS_at,
    KC(62),	KS_ampersand,	KS_1,		KS_exclamdown,
    KC(63),	KS_dollar,	KS_sterling,
		KS_guillemotleft,KS_guillemotright,
    KC(88),	KS_exclam,	KS_8,		KS_bracketleft,	KS_braceleft,
    KC(89),	KS_ccedilla,	KS_9,		KS_bracketright,KS_braceright,
    KC(90),	KS_agrave,	KS_0,		KS_questiondown,
    KC(91),	KS_parenright,	KS_degree,	KS_macron,
    KC(92),	KS_minus,	KS_underscore,	KS_plusminus,
    KC(99),	KS_dead_circumflex, KS_dead_diaeresis,	KS_dead_abovering,
    KC(100),	KS_grave,	KS_asterisk,	KS_bar,		KS_brokenbar,
    KC(101),	KS_less,	KS_greater,	KS_mu,
    KC(107),	KS_m,
    KC(108),	KS_ugrave,	KS_percent,	KS_grave,	KS_apostrophe,
    KC(112),	KS_comma,	KS_question,	KS_masculine,
    KC(113),	KS_semicolon,	KS_period,	KS_less,
    KC(114),	KS_colon,	KS_slash,	KS_greater,
    KC(115),	KS_equal,	KS_plus,	KS_underscore
};

static const keysym_t hilkbd_keydesc_fr_ps2[] = {
    KC(24),	KS_b,
    KC(25),	KS_v,
    KC(26),	KS_c,
    KC(27),	KS_x,
    KC(28),	KS_w,
    KC(40),	KS_h,
    KC(41),	KS_g,
    KC(42),	KS_f,
    KC(43),	KS_d,
    KC(44),	KS_s,
    KC(45),	KS_q,
    KC(48),	KS_u,
    KC(49),	KS_y,
    KC(50),	KS_t,
    KC(51),	KS_r,
    KC(52),	KS_e,
    KC(53),	KS_z,
    KC(54),	KS_a,
    KC(56),	KS_egrave,	KS_7,		KS_grave,
    KC(57),	KS_minus,	KS_6,		KS_bar,
    KC(58),	KS_parenleft,	KS_5,		KS_bracketleft,
    KC(59),	KS_apostrophe,	KS_4,		KS_braceleft,
    KC(60),	KS_quotedbl,	KS_3,		KS_numbersign,
    KC(61),	KS_eacute,	KS_2,		KS_asciitilde,
    KC(62),	KS_ampersand,	KS_1,
    KC(63),	KS_twosuperior,
    KC(88),	KS_underscore,	KS_8,		KS_backslash,
    KC(89),	KS_ccedilla,	KS_9,		KS_asciicircum,
    KC(90),	KS_agrave,	KS_0,		KS_at,
    KC(91),	KS_parenright,	KS_degree,	KS_bracketright,
    KC(92),	KS_equal,	KS_plus,	KS_braceright,
    KC(96),	KS_i,
    KC(97),	KS_o,
    KC(98),	KS_p,
    KC(99),	KS_dead_circumflex,KS_dead_diaeresis,
    KC(100),	KS_dollar,	KS_sterling,	KS_currency,
    KC(104),	KS_j,
    KC(105),	KS_k,
    KC(106),	KS_l,
    KC(107),	KS_m,
    KC(108),	KS_ugrave,	KS_percent,
    KC(112),	KS_comma,	KS_question,
    KC(113),	KS_semicolon,	KS_period,
    KC(114),	KS_colon,	KS_slash,
    KC(115),	KS_exclam,	KS_section,
    KC(116),	KS_asterisk,	KS_mu,
    KC(118),	KS_less,	KS_greater,
		KS_guillemotleft,KS_guillemotright,
    KC(120),	KS_n
};

#define KBD_MAP(name, base, map) \
			{ name, base, sizeof(map)/sizeof(keysym_t), map }

const struct wscons_keydesc hilkbd_keydesctab[] = {
	KBD_MAP(KB_US,			0,	hilkbd_keydesc_us),
	KBD_MAP(KB_DE,			KB_US,	hilkbd_keydesc_de),
	KBD_MAP(KB_DE | KB_NODEAD,	KB_DE,	hilkbd_keydesc_de_nodead),
	KBD_MAP(KB_FR,			KB_US,	hilkbd_keydesc_fr),
	KBD_MAP(KB_UK,			KB_US,	hilkbd_keydesc_uk),
	KBD_MAP(KB_SV,			KB_US,	hilkbd_keydesc_sv),
	KBD_MAP(KB_SV | KB_NODEAD,	KB_SV,	hilkbd_keydesc_sv_nodead),
	{0, 0, 0, 0},
};

const struct wscons_keydesc hilkbd_keydesctab_ps2[] = {
	KBD_MAP(KB_US,			0,	hilkbd_keydesc_us),
	KBD_MAP(KB_DE,			KB_US,	hilkbd_keydesc_de_ps2),
	KBD_MAP(KB_DE | KB_NODEAD,	KB_DE,	hilkbd_keydesc_de_ps2_nodead),
	KBD_MAP(KB_FR,			KB_US,	hilkbd_keydesc_fr_ps2),
	KBD_MAP(KB_UK,			KB_US,	hilkbd_keydesc_uk_ps2),
	KBD_MAP(KB_SV,			KB_US,	hilkbd_keydesc_sv_ps2),
	KBD_MAP(KB_SV | KB_NODEAD,	KB_SV,	hilkbd_keydesc_sv_ps2_nodead),
	{0, 0, 0, 0},
};

/*
 * Keyboard ID to layout table
 */
const kbd_t hilkbd_layouts[MAXHILKBDLAYOUT] = {
	-1,	/* 00 Undefined or custom layout */
	-1,	/* 01 Undefined */
	-1,	/* 02 Japanese */
	-1,	/* 03 Swiss french */
	-1,	/* 04 Portuguese */
	-1,	/* 05 Arabic */
	-1,	/* 06 Hebrew */
	-1,	/* 07 Canadian English */
	-1,	/* 08 Turkish */
	-1,	/* 09 Greek */
	-1,	/* 0a Thai */
	-1,	/* 0b Italian */
	-1,	/* 0c Korean */
	-1,	/* 0d Dutch */
	KB_SV,	/* 0e Swedish */
	KB_DE,	/* 0f German */
	-1,	/* 10 Simplified Chinese */
	-1,	/* 11 Traditional Chinese */
	-1,	/* 12 Swiss French 2 */
	-1,	/* 13 Euro Spanish */
	-1,	/* 14 Swiss German 2 */
	-1,	/* 15 Belgian */
	-1,	/* 16 Finnish */
	KB_UK,	/* 17 UK English */
	-1,	/* 18 Canadian French */
	-1,	/* 19 Swiss German */
	-1,	/* 1a Norwegian */
	KB_FR,	/* 1b French */
	-1,	/* 1c Danish */
	-1,	/* 1d Katakana */
	-1,	/* 1e Latin Spanish */
	KB_US,	/* 1f US ASCII */
};


code:
/*	$OpenBSD: hilkbdmap.h,v 1.3 2005/05/07 22:42:30 miod Exp $	*/
/*
 * Copyright (c) 2003, Miodrag Vallat.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

extern const struct wscons_keydesc hilkbd_keydesctab[];
extern const struct wscons_keydesc hilkbd_keydesctab_ps2[];

#define	MAXHILKBDLAYOUT	0x20

extern const kbd_t hilkbd_layouts[MAXHILKBDLAYOUT];
extern const u_int8_t hilkbd_raw[0x80];

license: unknown


code:
/*	$OpenBSD: hilms.c,v 1.5 2007/04/10 22:37:17 miod Exp $	*/
/*
 * Copyright (c) 2003, Miodrag Vallat.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/ioctl.h>

#include <machine/autoconf.h>
#include <machine/bus.h>
#include <machine/cpu.h>

#include <dev/hil/hilreg.h>
#include <dev/hil/hilvar.h>
#include <dev/hil/hildevs.h>

#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsmousevar.h>

struct hilms_softc {
	struct hildev_softc sc_hildev;

	int		sc_features;
	u_int		sc_buttons;
	u_int		sc_axes;
	int		sc_enabled;
	int		sc_buttonstate;

	struct device	*sc_wsmousedev;
};

int	hilmsprobe(struct device *, void *, void *);
void	hilmsattach(struct device *, struct device *, void *);
int	hilmsdetach(struct device *, int);

struct cfdriver hilms_cd = {
	NULL, "hilms", DV_DULL
};

struct cfattach hilms_ca = {
	sizeof(struct hilms_softc), hilmsprobe, hilmsattach, hilmsdetach,
};

int	hilms_enable(void *);
int	hilms_ioctl(void *, u_long, caddr_t, int, struct proc *);
void	hilms_disable(void *);

const struct wsmouse_accessops hilms_accessops = {
	hilms_enable,
	hilms_ioctl,
	hilms_disable,
};

void	hilms_callback(struct hildev_softc *, u_int, u_int8_t *);

int
hilmsprobe(struct device *parent, void *match, void *aux)
{
	struct hil_attach_args *ha = aux;

	if (ha->ha_type != HIL_DEVICE_MOUSE)
		return (0);

	/*
	 * Reject anything that has only buttons - they are handled as
	 * keyboards, really.
	 */
	if (ha->ha_infolen > 1 && (ha->ha_info[1] & HIL_AXMASK) == 0)
		return (0);

	return (1);
}

void
hilmsattach(struct device *parent, struct device *self, void *aux)
{
	struct hilms_softc *sc = (void *)self;
	struct hil_attach_args *ha = aux;
	struct wsmousedev_attach_args a;
	int iob, rx, ry;

	sc->hd_code = ha->ha_code;
	sc->hd_type = ha->ha_type;
	sc->hd_infolen = ha->ha_infolen;
	bcopy(ha->ha_info, sc->hd_info, ha->ha_infolen);
	sc->hd_fn = hilms_callback;

	/*
	 * Interpret the identification bytes, if any
	 */
	rx = ry = 0;
	if (ha->ha_infolen > 1) {
		sc->sc_features = ha->ha_info[1];
		sc->sc_axes = sc->sc_features & HIL_AXMASK;

		if (sc->sc_features & HIL_IOB) {
			/* skip resolution bytes */
			iob = 4;
			if (sc->sc_features & HIL_ABSOLUTE) {
				/* skip ranges */
				rx = ha->ha_info[4] | (ha->ha_info[5] << 8);
				if (sc->sc_axes > 1)
					ry = ha->ha_info[6] |
					    (ha->ha_info[7] << 8);
				iob += 2 * sc->sc_axes;
			}

			if (iob >= ha->ha_infolen) {
				sc->sc_features &= ~(HIL_IOB | HILIOB_PIO);
			} else {
				iob = ha->ha_info[iob];
				sc->sc_buttons = iob & HILIOB_BMASK;
				sc->sc_features |= (iob & HILIOB_PIO);
			}
		}
	}

	printf(", %d axes", sc->sc_axes);
	if (sc->sc_buttons == 1)
		printf(", 1 button");
	else if (sc->sc_buttons > 1)
		printf(", %d buttons", sc->sc_buttons);
	if (sc->sc_features & HILIOB_PIO)
		printf(", pressure sensor");
	if (sc->sc_features & HIL_ABSOLUTE) {
		printf ("\n%s: %d", self->dv_xname, rx);
		if (ry != 0)
			printf("x%d", ry);
		else
			printf(" linear");
		printf(" fixed area");
	}

	printf("\n");

	sc->sc_enabled = 0;

	a.accessops = &hilms_accessops;
	a.accesscookie = sc;

	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
}

int
hilmsdetach(struct device *self, int flags)
{
	struct hilms_softc *sc = (void *)self;

	if (sc->sc_wsmousedev != NULL)
		return config_detach(sc->sc_wsmousedev, flags);

	return (0);
}

int
hilms_enable(void *v)
{
	struct hilms_softc *sc = v;

	if (sc->sc_enabled)
		return EBUSY;

	sc->sc_enabled = 1;
	sc->sc_buttonstate = 0;

	return (0);
}

void
hilms_disable(void *v)
{
	struct hilms_softc *sc = v;

	sc->sc_enabled = 0;
}

int
hilms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
{
#if 0
	struct hilms_softc *sc = v;
#endif

	switch (cmd) {
	case WSMOUSEIO_GTYPE:
		*(int *)data = WSMOUSE_TYPE_HIL;
		return 0;
	}

	return -1;
}

void
hilms_callback(struct hildev_softc *dev, u_int buflen, u_int8_t *buf)
{
	struct hilms_softc *sc = (struct hilms_softc *)dev;
	int type, flags;
	int dx, dy, dz, button;
#ifdef DIAGNOSTIC
	int minlen;
#endif

	/*
	 * Ignore packet if we don't need it
	 */
	if (sc->sc_enabled == 0)
		return;

	type = *buf++;

#ifdef DIAGNOSTIC
	/*
	 * Check that the packet contains all the expected data,
	 * ignore it if too short.
	 */
	minlen = 1;
	if (type & HIL_MOUSEMOTION) {
		minlen += sc->sc_axes <<
		    (sc->sc_features & HIL_16_BITS) ? 1 : 0;
	}
	if (type & HIL_MOUSEBUTTON)
		minlen++;

	if (minlen > buflen)
		return;
#endif

	/*
	 * The packet can contain both a mouse motion and a button event.
	 * In this case, the motion data comes first.
	 */

	if (type & HIL_MOUSEMOTION) {
		flags = sc->sc_features & HIL_ABSOLUTE ?
		    WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y |
		    WSMOUSE_INPUT_ABSOLUTE_Z : WSMOUSE_INPUT_DELTA;
		if (sc->sc_features & HIL_16_BITS) {
			dx = *buf++;
			dx |= (*buf++) << 8;
			if (!(sc->sc_features & HIL_ABSOLUTE))
				dx = (int16_t)dx;
		} else {
			dx = *buf++;
			if (!(sc->sc_features & HIL_ABSOLUTE))
				dx = (int8_t)dx;
		}
		if (sc->sc_axes > 1) {
			if (sc->sc_features & HIL_16_BITS) {
				dy = *buf++;
				dy |= (*buf++) << 8;
				if (!(sc->sc_features & HIL_ABSOLUTE))
					dy = (int16_t)dy;
			} else {
				dy = *buf++;
				if (!(sc->sc_features & HIL_ABSOLUTE))
					dy = (int8_t)dy;
			}
			if (sc->sc_axes > 2) {
				if (sc->sc_features & HIL_16_BITS) {
					dz = *buf++;
					dz |= (*buf++) << 8;
					if (!(sc->sc_features & HIL_ABSOLUTE))
						dz = (int16_t)dz;
				} else {
					dz = *buf++;
					if (!(sc->sc_features & HIL_ABSOLUTE))
						dz = (int8_t)dz;
				}
			} else
				dz = 0;
		} else
			dy = dz = 0;

		/*
		 * Correct Y direction for button boxes.
		 */
		if ((sc->sc_features & HIL_ABSOLUTE) == 0 &&
		    sc->sc_buttons == 0)
			dy = -dy;
	} else
		dx = dy = dz = flags = 0;

	if (type & HIL_MOUSEBUTTON) {
		button = *buf;
		/*
		 * The pressure sensor is very primitive and only has
		 * a boolean behaviour, as an extra mouse button, which is
		 * down if there is pressure or the pen is near the tablet,
		 * and up if there is no pressure or the pen is far from the
		 * tablet - at least for Tablet id 0x94, P/N 46088B
		 *
		 * The corresponding codes are 0x8f and 0x8e. Convert them
		 * to a pseudo fourth button - even if the tablet never
		 * has three buttons.
		 */
		button = (button - 0x80) >> 1;
		if (button > 4)
			button = 4;

		if (*buf & 1) {
			/* Button released, or no pressure */
			sc->sc_buttonstate &= ~(1 << button);
		} else {
			/* Button pressed, or pressure */
			sc->sc_buttonstate |= (1 << button);
		}
		/* buf++; */
	}

	if (sc->sc_wsmousedev != NULL)
		wsmouse_input(sc->sc_wsmousedev,
		    sc->sc_buttonstate, dx, dy, dz, 0, flags);
}


code:
/*	$OpenBSD: hilreg.h,v 1.6 2006/08/10 23:43:45 miod Exp $	*/
/*	$NetBSD: hilreg.h,v 1.6 1997/02/02 09:39:21 thorpej Exp $	*/

/*
 * Copyright (c) 1988 University of Utah.
 * Copyright (c) 1990, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * the Systems Programming Group of the University of Utah Computer
 * Science Department.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * from: Utah $Hdr: hilreg.h 1.10 92/01/21$
 *
 *	@(#)hilreg.h	8.1 (Berkeley) 6/10/93
 */

#include <machine/hil_machdep.h>

#define	HIL_BUSY		0x02
#define	HIL_DATA_RDY		0x01

/* HIL status bits */
#define	HIL_POLLDATA	0x10		/* HIL poll data follows */
#define	HIL_COMMAND	0x08		/* Start of original command */
#define	HIL_ERROR	0x80		/* HIL error */

#define	HIL_RECONFIG	0x80		/* HIL has reconfigured */
#define	HIL_UNPLUGGED	0x84		/* HIL cable unplugged */

#define	HIL_SSHIFT	4		/* Bits to shift status over */
#define	HIL_SMASK	0x0f		/* Service request status mask */
#define	HIL_DEVMASK	0x07

/* HIL status types */
#define	HIL_68K		0x04		/* Data from the 68k is ready */
#define	HIL_STATUS	0x05		/* HIL status in data register */
#define	HIL_DATA	0x06		/* HIL data in data register */
#define	HIL_CTRLSHIFT	0x08		/* key + CTRL + SHIFT */
#define	HIL_CTRL	0x09		/* key + CTRL */
#define	HIL_SHIFT	0x0a		/* key + SHIFT */
#define	HIL_KEY		0x0b		/* key only */

/* HIL commands */
#define HIL_IDENTIFY	0x03		/* Get device information */
#define	HIL_READTIME	0x13		/* Read real time register */
#define	HIL_RNAME	0x30		/* Report name */
#define	HIL_RSTATUS	0x31		/* Report status */
#define	HIL_DESCRIBE	0x32		/* Extended describe */
#define HIL_SECURITY	0x33		/* Read security bits */
#define	HIL_DKR		0x3d		/* Disable auto repeat */
#define	HIL_ER1		0x3e		/* Enable auto repeat 1/30 */
#define	HIL_ER2		0x3f		/* Enable auto repeat 1/60 */
#define	HIL_PROMPT1	0x40		/* Prompt #1 */
#define	HIL_PROMPT2	0x41		/* Prompt #2 */
#define	HIL_PROMPT3	0x42		/* Prompt #3 */
#define	HIL_PROMPT4	0x43		/* Prompt #4 */
#define	HIL_PROMPT5	0x44		/* Prompt #5 */
#define	HIL_PROMPT6	0x45		/* Prompt #6 */
#define	HIL_PROMPT7	0x46		/* Prompt #7 */
#define	HIL_PROMPT	0x47		/* Prompt */
#define	HIL_ACK1	0x48		/* Acknowledge #1 */
#define	HIL_ACK2	0x49		/* Acknowledge #2 */
#define	HIL_ACK3	0x4a		/* Acknowledge #3 */
#define	HIL_ACK4	0x4b		/* Acknowledge #4 */
#define	HIL_ACK5	0x4c		/* Acknowledge #5 */
#define	HIL_ACK6	0x4d		/* Acknowledge #6 */
#define	HIL_ACK7	0x4e		/* Acknowledge #7 */
#define	HIL_ACK		0x4f		/* Acknowledge */
#define	HIL_INTON	0x5c		/* Turn on interrupts. */
#define	HIL_INTOFF	0x5d		/* Turn off interrupts. */
#define	HIL_SETARD	0xa0		/* Set auto-repeat delay */
#define	HIL_SETARR	0xa2		/* Set auto-repeat rate */
#define	HIL_SETTONE	0xa3		/* Set tone generator */
#define	HIL_CNMT	0xb2		/* Clear nmi */
#define	HIL_TRIGGER	0xc5		/* Trigger command */
#define	HIL_STARTCMD	0xe0		/* Start loop command */
#define	HIL_TIMEOUT	0xfe		/* Timeout */

/* Read/write various registers on the 8042. */
#define	HIL_READBUSY		0x02	/* internal "busy" register */
#define	HIL_READKBDLANG		0x12	/* read keyboard language code */
#define	HIL_WRITEKBDSADR 	0xe9
#define	HIL_WRITELPSTAT 	0xea
#define	HIL_WRITELPCTRL 	0xeb
#define	HIL_READKBDSADR	 	0xf9
#define	HIL_READLPSTAT  	0xfa
#define	HIL_READLPCTRL  	0xfb

/* BUSY bits */
#define	BSY_LOOPBUSY	0x04

/* LPCTRL bits */
#define	LPC_AUTOPOLL	0x01	/* enable auto-polling */
#define	LPC_NOERROR	0x02	/* don't report errors */
#define	LPC_NORECONF	0x04	/* don't report reconfigure */
#define	LPC_KBDCOOK	0x10	/* cook all keyboards */
#define	LPC_RECONF	0x80	/* reconfigure the loop */

/* LPSTAT bits */
#define	LPS_DEVMASK	0x07	/* number of loop devices */
#define	LPS_CONFGOOD	0x08	/* reconfiguration worked */
#define	LPS_CONFFAIL	0x80	/* reconfiguration failed */

/* HIL packet headers */
#define	HIL_MOUSEDATA   0x02
#define	HIL_KBDDATA     0x70

#define	HIL_MOUSEMOTION	0x02	/* mouse movement event */
#define	HIL_TABLET      0x02	/* tablet motion event */
#define	HIL_KNOBBOX     0x03	/* knob box motion data */
#define	HIL_KBDBUTTON	0x40	/* keyboard button event */
#define	HIL_MOUSEBUTTON 0x40	/* mouse button event */
#define	HIL_BUTTONBOX   0x60	/* button box event */

/* ID module defines */
#define HILSCBIT	0x04

/* For setting auto repeat on the keyboard */
#define	ar_format(x)	~((x - 10) / 10)
#define	KBD_ARD		400	/* initial delay in msec (10 - 2560) */
#define	KBD_ARR		60	/* rate (10 - 2550 msec, 2551 == off) */

/* Device information bits */
#define	HIL_ABSOLUTE	0x40	/* absolute positioning data */
#define	HIL_16_BITS	0x20	/* 16 bit position accuracy */
#define	HIL_IOB		0x10	/* I/O description byte follows */
#define	HIL_AXMASK	0x03	/* Number of axes supported */

#define	HILIOB_PROMPT	0x80	/* prompt and acknowledge (leds) supported */
#define	HILIOB_PMASK	0x70	/* number of prompt & acknowledge supported */
#define	HILIOB_PIO	0x08	/* proximity in/out (pressure) supported */
#define	HILIOB_BMASK	0x07	/* number of buttons supported */


code:
/*	$OpenBSD: hilvar.h,v 1.10 2006/11/05 14:39:32 miod Exp $	*/
/*
 * Copyright (c) 2003, Miodrag Vallat.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

/*
 * Copyright (c) 1988 University of Utah.
 * Copyright (c) 1990, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * the Systems Programming Group of the University of Utah Computer
 * Science Department.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * from: Utah $Hdr: hilvar.h 1.3 92/01/21$
 *
 *	@(#)hilvar.h	8.1 (Berkeley) 6/10/93
 */

#define NHILD		8		/* 7 actual + loop pseudo (dev 0) */

struct hildev_softc;

struct hil_softc {
	struct device	sc_dev;
	bus_space_handle_t sc_bsh;
	bus_space_tag_t	sc_bst;
	int		*sc_console;	/* console path set to hil */

	struct proc	*sc_thread;	/* event handling thread */

	int		sc_cmddone;
	int		sc_cmdending;
	u_int		sc_actdev;	/* current input device */
	u_int		sc_cmddev;	/* device to perform command on */
	u_int8_t	sc_pollbuf[HILBUFSIZE];	/* interrupt time input buf */
	u_int8_t	sc_cmdbuf[HILBUFSIZE];
	u_int8_t	*sc_pollbp;	/* pointer into sc_pollbuf */
	u_int8_t	*sc_cmdbp;	/* pointer into sc_cmdbuf */

	int		sc_status;	/* initialization status */
#define	HIL_STATUS_BUSY		0x00
#define	HIL_STATUS_READY	0x01
	int		sc_pending;	/* reconfiguration events in progress */
#define	HIL_PENDING_RECONFIG	0x01
#define	HIL_PENDING_UNPLUGGED	0x02
	u_int		sc_maxdev;	/* number of devices on loop */
	struct hildev_softc *sc_devices[NHILD];	/* interrupt dispatcher */
};

#ifdef _KERNEL

int	send_hil_cmd(struct hil_softc *, u_int, u_int8_t *, u_int, u_int8_t *);
int	send_hildev_cmd(struct hildev_softc *, u_int, u_int8_t *, u_int *);
void	hil_set_poll(struct hil_softc *, int);
int	hil_poll_data(struct hildev_softc *, u_int8_t *, u_int8_t *);

void	hil_attach(struct hil_softc *, int *);
void	hil_attach_deferred(void *);
int	hil_intr(void *);
int	hildevprint(void *, const char *);

#endif /* _KERNEL */


code:
/*	$OpenBSD: hotplug.c,v 1.15 2015/03/14 03:38:46 jsg Exp $	*/
/*
 * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Device attachment and detachment notifications.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/fcntl.h>
#include <sys/hotplug.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/vnode.h>

#define HOTPLUG_MAXEVENTS	64

static int opened;
static struct hotplug_event evqueue[HOTPLUG_MAXEVENTS];
static int evqueue_head, evqueue_tail, evqueue_count;
static struct selinfo hotplug_sel;

void filt_hotplugrdetach(struct knote *);
int  filt_hotplugread(struct knote *, long);

struct filterops hotplugread_filtops =
	{ 1, NULL, filt_hotplugrdetach, filt_hotplugread};

#define EVQUEUE_NEXT(p) (p == HOTPLUG_MAXEVENTS - 1 ? 0 : p + 1)

int hotplug_put_event(struct hotplug_event *);
int hotplug_get_event(struct hotplug_event *);

void hotplugattach(int);

void
hotplugattach(int count)
{
	opened = 0;
	evqueue_head = 0;
	evqueue_tail = 0;
	evqueue_count = 0;
}

void
hotplug_device_attach(enum devclass class, char *name)
{
	struct hotplug_event he;

	he.he_type = HOTPLUG_DEVAT;
	he.he_devclass = class;
	strlcpy(he.he_devname, name, sizeof(he.he_devname));
	hotplug_put_event(&he);
}

void
hotplug_device_detach(enum devclass class, char *name)
{
	struct hotplug_event he;

	he.he_type = HOTPLUG_DEVDT;
	he.he_devclass = class;
	strlcpy(he.he_devname, name, sizeof(he.he_devname));
	hotplug_put_event(&he);
}

int
hotplug_put_event(struct hotplug_event *he)
{
	if (evqueue_count == HOTPLUG_MAXEVENTS && opened) {
		printf("hotplug: event lost, queue full\n");
		return (1);
	}

	evqueue[evqueue_head] = *he;
	evqueue_head = EVQUEUE_NEXT(evqueue_head);
	if (evqueue_count == HOTPLUG_MAXEVENTS)
		evqueue_tail = EVQUEUE_NEXT(evqueue_tail);
	else
		evqueue_count++;
	wakeup(&evqueue);
	selwakeup(&hotplug_sel);
	return (0);
}

int
hotplug_get_event(struct hotplug_event *he)
{
	int s;

	if (evqueue_count == 0)
		return (1);

	s = splbio();
	*he = evqueue[evqueue_tail];
	evqueue_tail = EVQUEUE_NEXT(evqueue_tail);
	evqueue_count--;
	splx(s);
	return (0);
}

int
hotplugopen(dev_t dev, int flag, int mode, struct proc *p)
{
	if (minor(dev) != 0)
		return (ENXIO);
	if ((flag & FWRITE))
		return (EPERM);
	if (opened)
		return (EBUSY);
	opened = 1;
	return (0);
}

int
hotplugclose(dev_t dev, int flag, int mode, struct proc *p)
{
	struct hotplug_event he;

	while (hotplug_get_event(&he) == 0)
		;
	opened = 0;
	return (0);
}

int
hotplugread(dev_t dev, struct uio *uio, int flags)
{
	struct hotplug_event he;
	int error;

	if (uio->uio_resid != sizeof(he))
		return (EINVAL);

again:
	if (hotplug_get_event(&he) == 0)
		return (uiomove(&he, sizeof(he), uio));
	if (flags & IO_NDELAY)
		return (EAGAIN);

	error = tsleep(&evqueue, PRIBIO | PCATCH, "htplev", 0);
	if (error)
		return (error);
	goto again;
}

int
hotplugioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	switch (cmd) {
	case FIOASYNC:
		/* ignore */
	case FIONBIO:
		/* handled in the upper fs layer */
		break;
	default:
		return (ENOTTY);
	}

	return (0);
}

int
hotplugpoll(dev_t dev, int events, struct proc *p)
{
	int revents = 0;

	if (events & (POLLIN | POLLRDNORM)) {
		if (evqueue_count > 0)
			revents |= events & (POLLIN | POLLRDNORM);
		else
			selrecord(p, &hotplug_sel);
	}

	return (revents);
}

int
hotplugkqfilter(dev_t dev, struct knote *kn)
{
	struct klist *klist;
	int s;

	switch (kn->kn_filter) {
	case EVFILT_READ:
		klist = &hotplug_sel.si_note;
		kn->kn_fop = &hotplugread_filtops;
		break;
	default:
		return (EINVAL);
	}

	s = splbio();
	SLIST_INSERT_HEAD(klist, kn, kn_selnext);
	splx(s);
	return (0);
}

void
filt_hotplugrdetach(struct knote *kn)
{
	int s;

	s = splbio();
	SLIST_REMOVE(&hotplug_sel.si_note, kn, knote, kn_selnext);
	splx(s);
}

int
filt_hotplugread(struct knote *kn, long hint)
{
	kn->kn_data = evqueue_count;

	return (evqueue_count > 0);
}


code:
/*	$OpenBSD: ad741x.c,v 1.14 2008/04/17 19:01:48 deraadt Exp $	*/

/*
 * Copyright (c) 2005 Theo de Raadt
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sensors.h>

#include <dev/i2c/i2cvar.h>

/* AD741x registers */
#define AD741X_TEMP	0x00
#define AD741X_CONFIG	0x01
#define AD741X_THYST	0x02
#define AD741X_TOTI	0x03
#define AD741X_ADC	0x04
#define AD741X_CONFIG2	0x05

#define AD741X_CONFMASK	0xe0

/* Sensors */
#define ADC_TEMP		0
#define ADC_ADC0		1
#define ADC_ADC1		2
#define ADC_ADC2		3
#define ADC_ADC3		4
#define ADC_MAX_SENSORS		5

struct adc_softc {
	struct device	sc_dev;
	i2c_tag_t	sc_tag;
	i2c_addr_t	sc_addr;
	int		sc_chip;
	u_int8_t	sc_config;

	struct ksensor sc_sensor[ADC_MAX_SENSORS];
	struct ksensordev sc_sensordev;
};

int	adc_match(struct device *, void *, void *);
void	adc_attach(struct device *, struct device *, void *);
void	adc_refresh(void *);

struct cfattach adc_ca = {
	sizeof(struct adc_softc), adc_match, adc_attach
};

struct cfdriver adc_cd = {
	NULL, "adc", DV_DULL
};

int
adc_match(struct device *parent, void *match, void *aux)
{
	struct i2c_attach_args *ia = aux;

	if (strcmp(ia->ia_name, "ad7417") == 0 ||
	    strcmp(ia->ia_name, "ad7418") == 0)
		return (1);
	return (0);
}

void
adc_attach(struct device *parent, struct device *self, void *aux)
{
	struct adc_softc *sc = (struct adc_softc *)self;
	struct i2c_attach_args *ia = aux;
	u_int8_t cmd, data;
	int nsens = 0, i;

	sc->sc_tag = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	printf(": %s", ia->ia_name);

	sc->sc_chip = 0;
	if (strcmp(ia->ia_name, "ad7417") == 0)
		sc->sc_chip = 7417;
	if (strcmp(ia->ia_name, "ad7418") == 0)
		sc->sc_chip = 7418;

	if (sc->sc_chip != 0) {
		cmd = AD741X_CONFIG2;
		data = 0;
		if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
			printf(", config2 reset failed\n");
			return;
		}
	}

	cmd = AD741X_CONFIG;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
		printf(", config reset failed\n");
		return;
	}
	data &= 0xfe;
	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
		printf(", config reset failed\n");
		return;
	}
	sc->sc_config = data;

	/* Initialize sensor data. */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));

	sc->sc_sensor[ADC_TEMP].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ADC_TEMP].desc, "Internal",
	    sizeof(sc->sc_sensor[ADC_TEMP].desc));
	nsens = 1;

	if (sc->sc_chip == 7417 || sc->sc_chip == 7418) {
		sc->sc_sensor[ADC_ADC0].type = SENSOR_INTEGER;
		nsens++;
	}
	if (sc->sc_chip == 7417 || sc->sc_chip == 7418) {
		sc->sc_sensor[ADC_ADC1].type = SENSOR_INTEGER;
		sc->sc_sensor[ADC_ADC2].type = SENSOR_INTEGER;
		sc->sc_sensor[ADC_ADC3].type = SENSOR_INTEGER;
		nsens += 3;
	}

	if (sensor_task_register(sc, adc_refresh, 5) == NULL) {
		printf(", unable to register update task\n");
		return;
	}

	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[0]);
	if (sc->sc_chip == 7417 || sc->sc_chip == 7418)
		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[1]);
	if (sc->sc_chip == 7417)
		for (i = 2; i < nsens; i++)
			sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
	sensordev_install(&sc->sc_sensordev);

	printf("\n");
}

void
adc_refresh(void *arg)
{
	struct adc_softc *sc = arg;
	u_int8_t cmd, reg;
	u_int16_t data;
	int i;

	iic_acquire_bus(sc->sc_tag, 0);

	reg = (sc->sc_config & AD741X_CONFMASK) | (0 << 5);
	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &reg, sizeof reg, 0))
		goto done;
	delay(1000);
	cmd = AD741X_TEMP;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0))
		goto done;
	sc->sc_sensor[ADC_TEMP].value = 273150000 +
	    (betoh16(data) >> 6) * 250000;

	if (sc->sc_chip == 0)
		goto done;

	if (sc->sc_chip == 7418) {
		reg = (reg & AD741X_CONFMASK) | (4 << 5);
		if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &reg, sizeof reg, 0))
			goto done;
		delay(1000);
		cmd = AD741X_ADC;
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0))
			goto done;
		sc->sc_sensor[ADC_ADC0].value = betoh16(data) >> 6;
		goto done;
	}

	for (i = 0; i < 4; i++) {
		reg = (reg & AD741X_CONFMASK) | (i << 5);
		if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &reg, sizeof reg, 0))
			goto done;
		delay(1000);
		cmd = AD741X_ADC;
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0))
			goto done;
		sc->sc_sensor[ADC_ADC0 + i].value = betoh16(data) >> 6;
	}

done:
	iic_release_bus(sc->sc_tag, 0);
}


code:
/*	$OpenBSD: adm1021.c,v 1.28 2011/03/10 23:14:30 deraadt Exp $	*/

/*
 * Copyright (c) 2005 Theo de Raadt
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sensors.h>

#include <dev/i2c/i2cvar.h>

/* ADM 1021 registers */
#define ADM1021_INT_TEMP	0x00
#define ADM1021_EXT_TEMP	0x01
#define ADM1021_STATUS		0x02
#define  ADM1021_STATUS_INVAL	0x7f
#define  ADM1021_STATUS_NOEXT	0x40
#define ADM1021_CONFIG_READ	0x03
#define ADM1021_CONFIG_WRITE	0x09
#define  ADM1021_CONFIG_RUN	0x40
#define ADM1021_COMPANY		0xfe	/* contains 0x41 */
#define ADM1021_STEPPING	0xff	/* contains 0x3? */

/* Sensors */
#define ADMTEMP_EXT		0
#define ADMTEMP_INT		1
#define ADMTEMP_NUM_SENSORS	2

struct admtemp_softc {
	struct device	sc_dev;
	i2c_tag_t	sc_tag;
	i2c_addr_t	sc_addr;

	struct ksensor	sc_sensor[ADMTEMP_NUM_SENSORS];
	struct ksensordev sc_sensordev;
	int		sc_noexternal;
};

int	admtemp_match(struct device *, void *, void *);
void	admtemp_attach(struct device *, struct device *, void *);
void	admtemp_refresh(void *);

struct cfattach admtemp_ca = {
	sizeof(struct admtemp_softc), admtemp_match, admtemp_attach
};

struct cfdriver admtemp_cd = {
	NULL, "admtemp", DV_DULL
};

int
admtemp_match(struct device *parent, void *match, void *aux)
{
	struct i2c_attach_args *ia = aux;

	if (strcmp(ia->ia_name, "adm1021") == 0 ||
	    strcmp(ia->ia_name, "adm1023") == 0 ||
	    strcmp(ia->ia_name, "adm1032") == 0 ||
	    strcmp(ia->ia_name, "g781") == 0 ||
	    strcmp(ia->ia_name, "g781-1") == 0 ||
	    strcmp(ia->ia_name, "gl523sm") == 0 ||
	    strcmp(ia->ia_name, "max1617") == 0 ||
	    strcmp(ia->ia_name, "sa56004x") == 0 ||
	    strcmp(ia->ia_name, "xeontemp") == 0)
		return (1);
	return (0);
}

void
admtemp_attach(struct device *parent, struct device *self, void *aux)
{
	struct admtemp_softc *sc = (struct admtemp_softc *)self;
	struct i2c_attach_args *ia = aux;
	u_int8_t cmd, data, stat;
	int xeon = 0, i;

	sc->sc_tag = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	if (strcmp(ia->ia_name, "xeontemp") == 0) {
		printf(": Xeon");
		xeon = 1;
	} else
		printf(": %s", ia->ia_name);

	iic_acquire_bus(sc->sc_tag, 0);
	cmd = ADM1021_CONFIG_READ;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
		iic_release_bus(sc->sc_tag, 0);
		printf(", cannot get control register\n");
		return;
	}
	if (data & ADM1021_CONFIG_RUN) {
		cmd = ADM1021_STATUS;
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &stat, sizeof stat, 0)) {
			iic_release_bus(sc->sc_tag, 0);
			printf(", cannot read status register\n");
			return;
		}
		if ((stat & ADM1021_STATUS_INVAL) == ADM1021_STATUS_INVAL) {
			if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
			    sc->sc_addr, &cmd, sizeof cmd, &stat, sizeof stat, 0)) {
				iic_release_bus(sc->sc_tag, 0);
				printf(", cannot read status register\n");
				return;
			}
		}

		/* means external is dead */
		if ((stat & ADM1021_STATUS_INVAL) != ADM1021_STATUS_INVAL &&
		    (stat & ADM1021_STATUS_NOEXT))
			sc->sc_noexternal = 1;

		data &= ~ADM1021_CONFIG_RUN;
		cmd = ADM1021_CONFIG_WRITE;
		if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
			iic_release_bus(sc->sc_tag, 0);
			printf(", cannot set control register\n");
			return;
		}
	}
	iic_release_bus(sc->sc_tag, 0);

	/* Initialize sensor data. */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));

	sc->sc_sensor[ADMTEMP_EXT].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ADMTEMP_EXT].desc,
	    xeon ? "Xeon" : "External",
	    sizeof(sc->sc_sensor[ADMTEMP_EXT].desc));

	sc->sc_sensor[ADMTEMP_INT].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ADMTEMP_INT].desc,
	    xeon ? "Xeon" : "Internal",
	    sizeof(sc->sc_sensor[ADMTEMP_INT].desc));

	if (sensor_task_register(sc, admtemp_refresh, 5) == NULL) {
		printf(", unable to register update task\n");
		return;
	}

	for (i = 0; i < (sc->sc_noexternal ? 1 : ADMTEMP_NUM_SENSORS); i++)
		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
	sensordev_install(&sc->sc_sensordev);

	printf("\n");
}

void
admtemp_refresh(void *arg)
{
	struct admtemp_softc *sc = arg;
	u_int8_t cmd;
	int8_t sdata;

	iic_acquire_bus(sc->sc_tag, 0);

	if (sc->sc_noexternal == 0) {
		cmd = ADM1021_EXT_TEMP;
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
		    &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0) {
			if (sdata == 0x7f) {
				sc->sc_sensor[ADMTEMP_EXT].flags |= SENSOR_FINVALID;
			} else {
				sc->sc_sensor[ADMTEMP_EXT].value =
				    273150000 + 1000000 * sdata;
				sc->sc_sensor[ADMTEMP_EXT].flags &= ~SENSOR_FINVALID;
			}
		}
	} else
		sc->sc_sensor[ADMTEMP_EXT].flags |= SENSOR_FINVALID;

	cmd = ADM1021_INT_TEMP;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    &cmd, sizeof cmd, &sdata,  sizeof sdata, 0) == 0) {
		if (sdata == 0x7f) {
			sc->sc_sensor[ADMTEMP_INT].flags |= SENSOR_FINVALID;
		} else {
			sc->sc_sensor[ADMTEMP_INT].value =
			    273150000 + 1000000 * sdata;
			sc->sc_sensor[ADMTEMP_INT].flags &= ~SENSOR_FINVALID;
		}
	}

	iic_release_bus(sc->sc_tag, 0);
}


code:
/*	$OpenBSD: adm1024.c,v 1.14 2007/06/24 05:34:35 dlg Exp $	*/

/*
 * Copyright (c) 2005 Theo de Raadt
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sensors.h>

#include <dev/i2c/i2cvar.h>

/* ADM 1024 registers */
#define ADM1024_V2_5		0x20
#define ADM1024_Vccp		0x21
#define ADM1024_Vcc		0x22
#define ADM1024_V5		0x23
#define ADM1024_V12		0x24
#define ADM1024_Vccp2		0x25
#define ADM1024_EXT_TEMP	0x26
#define ADM1024_INT_TEMP	0x27
#define ADM1024_FAN1		0x28
#define ADM1024_FAN2		0x29
#define ADM1024_STATUS2		0x42
#define ADM1024_FANC		0x47
#define  ADM1024_STATUS2_EXT	0x40
#define ADM1024_COMPANY		0x3e	/* contains 0x41 */
#define ADM1024_STEPPING	0x3f	/* contains 0x2? */
#define ADM1024_CONFIG1		0x40
#define  ADM1024_CONFIG1_START	0x01
#define  ADM1024_CONFIG1_INTCLR	0x08

/* Sensors */
#define ADMLC_INT		0
#define ADMLC_EXT		1
#define ADMLC_V2_5		2
#define ADMLC_Vccp		3
#define ADMLC_Vcc		4
#define ADMLC_V5		5
#define ADMLC_V12		6
#define ADMLC_Vccp2		7
#define ADMLC_FAN1		8
#define ADMLC_FAN2		9
#define ADMLC_NUM_SENSORS	10

struct admlc_softc {
	struct device	sc_dev;
	i2c_tag_t	sc_tag;
	i2c_addr_t	sc_addr;

	struct ksensor	sc_sensor[ADMLC_NUM_SENSORS];
	struct ksensordev sc_sensordev;
	int		sc_fan1mul;
	int		sc_fan2mul;
};

int	admlc_match(struct device *, void *, void *);
void	admlc_attach(struct device *, struct device *, void *);
void	admlc_refresh(void *);

struct cfattach admlc_ca = {
	sizeof(struct admlc_softc), admlc_match, admlc_attach
};

struct cfdriver admlc_cd = {
	NULL, "admlc", DV_DULL
};

int
admlc_match(struct device *parent, void *match, void *aux)
{
	struct i2c_attach_args *ia = aux;

	if (strcmp(ia->ia_name, "adm1024") == 0)
		return (1);
	return (0);
}

void
admlc_attach(struct device *parent, struct device *self, void *aux)
{
	struct admlc_softc *sc = (struct admlc_softc *)self;
	struct i2c_attach_args *ia = aux;
	u_int8_t cmd, data, data2;
	int i;

	sc->sc_tag = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	iic_acquire_bus(sc->sc_tag, 0);
	cmd = ADM1024_CONFIG1;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
		iic_release_bus(sc->sc_tag, 0);
		printf(": cannot get control register\n");
		return;
	}
	data2 = data | ADM1024_CONFIG1_START;
	data2 = data2 & ~ADM1024_CONFIG1_INTCLR;
	if (data != data2) {
		if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data2, 0)) {
			iic_release_bus(sc->sc_tag, 0);
			printf(": cannot set control register\n");
			return;
		}
	}

	cmd = ADM1024_FANC;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
		printf(", unable to read fan setting\n");
		return;
	}
	sc->sc_fan1mul = (1 << (data >> 4) & 0x3);
	sc->sc_fan2mul = (1 << (data >> 6) & 0x3);

	iic_release_bus(sc->sc_tag, 0);

	/* Initialize sensor data. */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));

	sc->sc_sensor[ADMLC_INT].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ADMLC_INT].desc, "Internal",
	    sizeof(sc->sc_sensor[ADMLC_INT].desc));

	sc->sc_sensor[ADMLC_EXT].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ADMLC_EXT].desc, "External",
	    sizeof(sc->sc_sensor[ADMLC_EXT].desc));

	sc->sc_sensor[ADMLC_V2_5].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADMLC_V2_5].desc, "2.5 V",
	    sizeof(sc->sc_sensor[ADMLC_V2_5].desc));

	sc->sc_sensor[ADMLC_Vccp].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADMLC_Vccp].desc, "Vccp",
	    sizeof(sc->sc_sensor[ADMLC_Vccp].desc));

	sc->sc_sensor[ADMLC_Vcc].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADMLC_Vcc].desc, "Vcc",
	    sizeof(sc->sc_sensor[ADMLC_Vcc].desc));

	sc->sc_sensor[ADMLC_V5].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADMLC_V5].desc, "5 V",
	    sizeof(sc->sc_sensor[ADMLC_V5].desc));

	sc->sc_sensor[ADMLC_V12].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADMLC_V12].desc, "12 V",
	    sizeof(sc->sc_sensor[ADMLC_V12].desc));

	sc->sc_sensor[ADMLC_Vccp2].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADMLC_Vccp2].desc, "Vccp2",
	    sizeof(sc->sc_sensor[ADMLC_Vccp2].desc));

	sc->sc_sensor[ADMLC_FAN1].type = SENSOR_FANRPM;

	sc->sc_sensor[ADMLC_FAN2].type = SENSOR_FANRPM;

	if (sensor_task_register(sc, admlc_refresh, 5) == NULL) {
		printf(", unable to register update task\n");
		return;
	}

	for (i = 0; i < ADMLC_NUM_SENSORS; i++)
		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
	sensordev_install(&sc->sc_sensordev);

	printf("\n");
}

static void
fanval(struct ksensor *sens, int mul, u_int8_t data)
{
	int tmp = data * mul;

	if (tmp == 0)
		sens->flags |= SENSOR_FINVALID;
	else
		sens->value = 1350000 / tmp;
}

void
admlc_refresh(void *arg)
{
	struct admlc_softc *sc = arg;
	u_int8_t cmd, data;
	int8_t sdata;

	iic_acquire_bus(sc->sc_tag, 0);

	cmd = ADM1024_INT_TEMP;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0)
		sc->sc_sensor[ADMLC_INT].value = 273150000 + 1000000 * sdata;

	cmd = ADM1024_EXT_TEMP;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0)
		sc->sc_sensor[ADMLC_EXT].value = 273150000 + 1000000 * sdata;

	cmd = ADM1024_STATUS2;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0) {
		if (data & ADM1024_STATUS2_EXT)
			sc->sc_sensor[ADMLC_EXT].flags |= SENSOR_FINVALID;
		else
			sc->sc_sensor[ADMLC_EXT].flags &= ~SENSOR_FINVALID;
	}

	cmd = ADM1024_V2_5;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMLC_V2_5].value = 2500000 * data / 192;

	cmd = ADM1024_Vccp;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMLC_Vcc].value = 2249000 * data / 192;

	cmd = ADM1024_Vcc;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMLC_Vcc].value = 3300000 * data / 192;

	cmd = ADM1024_V5;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMLC_V5].value = 5000000 * data / 192;

	cmd = ADM1024_V12;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMLC_V12].value = 12000000 * data / 192;

	cmd = ADM1024_Vccp2;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMLC_Vccp2].value = 2700000 * data / 192;

	cmd = ADM1024_FAN1;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		fanval(&sc->sc_sensor[ADMLC_FAN1], sc->sc_fan1mul, data);

	cmd = ADM1024_FAN2;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		fanval(&sc->sc_sensor[ADMLC_FAN2], sc->sc_fan2mul, data);
	iic_release_bus(sc->sc_tag, 0);
}


code:
/*	$OpenBSD: adm1025.c,v 1.25 2007/06/24 05:34:35 dlg Exp $	*/

/*
 * Copyright (c) 2005 Theo de Raadt
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sensors.h>

#include <dev/i2c/i2cvar.h>

/* ADM 1025 registers */
#define ADM1025_V2_5		0x20
#define ADM1025_Vccp		0x21
#define ADM1025_V3_3		0x22
#define ADM1025_V5		0x23
#define ADM1025_V12		0x24
#define ADM1025_Vcc		0x25
#define ADM1025_EXT_TEMP	0x26
#define ADM1025_INT_TEMP	0x27
#define ADM1025_STATUS2		0x42
#define  ADM1025_STATUS2_EXT	0x40
#define ADM1025_COMPANY		0x3e	/* contains 0x41 */
#define ADM1025_STEPPING	0x3f	/* contains 0x2? */
#define ADM1025_CONFIG		0x40
#define  ADM1025_CONFIG_START	0x01
#define SMSC47M192_V1_5		0x50
#define SMSC47M192_V1_8		0x51
#define SMSC47M192_TEMP2	0x52

/* Sensors */
#define ADMTM_INT		0
#define ADMTM_EXT		1
#define ADMTM_V2_5		2
#define ADMTM_Vccp		3
#define ADMTM_V3_3		4
#define ADMTM_V5		5
#define ADMTM_V12		6
#define ADMTM_Vcc		7
#define ADMTM_NUM_SENSORS	8
#define SMSC_V1_5		8
#define SMSC_V1_8		9
#define SMSC_TEMP2		10
#define SMSC_NUM_SENSORS	3
struct admtm_softc {
	struct device	sc_dev;
	i2c_tag_t	sc_tag;
	i2c_addr_t	sc_addr;

	struct ksensor	sc_sensor[ADMTM_NUM_SENSORS + SMSC_NUM_SENSORS];
	struct ksensordev sc_sensordev;
	int		sc_nsensors;
	int		sc_model;
};

int	admtm_match(struct device *, void *, void *);
void	admtm_attach(struct device *, struct device *, void *);
void	admtm_refresh(void *);

struct cfattach admtm_ca = {
	sizeof(struct admtm_softc), admtm_match, admtm_attach
};

struct cfdriver admtm_cd = {
	NULL, "admtm", DV_DULL
};

int
admtm_match(struct device *parent, void *match, void *aux)
{
	struct i2c_attach_args *ia = aux;

	if (strcmp(ia->ia_name, "adm1025") == 0 ||
	    strcmp(ia->ia_name, "47m192") == 0 ||
	    strcmp(ia->ia_name, "ne1619") == 0)
		return (1);
	return (0);
}

void
admtm_attach(struct device *parent, struct device *self, void *aux)
{
	struct admtm_softc *sc = (struct admtm_softc *)self;
	struct i2c_attach_args *ia = aux;
	u_int8_t cmd, data, data2;
	int i;

	sc->sc_tag = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	printf(": %s", ia->ia_name);

	sc->sc_nsensors = ADMTM_NUM_SENSORS;
	sc->sc_model = 1025;
	if (strcmp(ia->ia_name, "47m192") == 0) {
		sc->sc_nsensors += SMSC_NUM_SENSORS;
		sc->sc_model = 192;
	}

	iic_acquire_bus(sc->sc_tag, 0);
	cmd = ADM1025_CONFIG;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
		iic_release_bus(sc->sc_tag, 0);
		printf(", cannot get control register\n");
		return;
	}

	data2 = data | ADM1025_CONFIG_START;
	if (data != data2) {
		if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data2, 0)) {
			iic_release_bus(sc->sc_tag, 0);
			printf(", cannot set control register\n");
			return;
		}
	}
	iic_release_bus(sc->sc_tag, 0);

	/* Initialize sensor data. */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));

	sc->sc_sensor[ADMTM_INT].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ADMTM_INT].desc, "Internal",
	    sizeof(sc->sc_sensor[ADMTM_INT].desc));

	sc->sc_sensor[ADMTM_EXT].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ADMTM_EXT].desc, "External",
	    sizeof(sc->sc_sensor[ADMTM_EXT].desc));

	sc->sc_sensor[ADMTM_V2_5].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADMTM_V2_5].desc, "2.5 V",
	    sizeof(sc->sc_sensor[ADMTM_V2_5].desc));

	sc->sc_sensor[ADMTM_Vccp].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADMTM_Vccp].desc, "Vccp",
	    sizeof(sc->sc_sensor[ADMTM_Vccp].desc));

	sc->sc_sensor[ADMTM_V3_3].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADMTM_V3_3].desc, "3.3 V",
	    sizeof(sc->sc_sensor[ADMTM_V3_3].desc));

	sc->sc_sensor[ADMTM_V5].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADMTM_V5].desc, "5 V",
	    sizeof(sc->sc_sensor[ADMTM_V5].desc));

	sc->sc_sensor[ADMTM_V12].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADMTM_V12].desc, "12 V",
	    sizeof(sc->sc_sensor[ADMTM_V12].desc));

	sc->sc_sensor[ADMTM_Vcc].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADMTM_Vcc].desc, "Vcc",
	    sizeof(sc->sc_sensor[ADMTM_Vcc].desc));

	sc->sc_sensor[SMSC_V1_5].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[SMSC_V1_5].desc, "1.5 V",
	    sizeof(sc->sc_sensor[SMSC_V1_5].desc));

	sc->sc_sensor[SMSC_V1_8].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[SMSC_V1_8].desc, "1.8 V",
	    sizeof(sc->sc_sensor[SMSC_V1_8].desc));

	sc->sc_sensor[SMSC_TEMP2].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[SMSC_TEMP2].desc, "External",
	    sizeof(sc->sc_sensor[SMSC_TEMP2].desc));

	if (sensor_task_register(sc, admtm_refresh, 5) == NULL) {
		printf(", unable to register update task\n");
		return;
	}

	for (i = 0; i < sc->sc_nsensors; i++)
		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
	sensordev_install(&sc->sc_sensordev);

	printf("\n");
}

void
admtm_refresh(void *arg)
{
	struct admtm_softc *sc = arg;
	u_int8_t cmd, data;
	int8_t sdata;

	iic_acquire_bus(sc->sc_tag, 0);

	cmd = ADM1025_INT_TEMP;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0)
		sc->sc_sensor[ADMTM_INT].value = 273150000 + 1000000 * sdata;

	cmd = ADM1025_EXT_TEMP;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0)
		sc->sc_sensor[ADMTM_EXT].value = 273150000 + 1000000 * sdata;

	cmd = ADM1025_STATUS2;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0) {
		if (data & ADM1025_STATUS2_EXT)
			sc->sc_sensor[ADMTM_EXT].flags |= SENSOR_FINVALID;
		else
			sc->sc_sensor[ADMTM_EXT].flags &= ~SENSOR_FINVALID;
	}

	cmd = ADM1025_V2_5;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMTM_V2_5].value = 2500000 * data / 192;

	cmd = ADM1025_Vccp;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMTM_Vcc].value = 2249000 * data / 192;

	cmd = ADM1025_V3_3;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMTM_V3_3].value = 3300000 * data / 192;

	cmd = ADM1025_V5;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMTM_V5].value = 5000000 * data / 192;

	cmd = ADM1025_V12;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMTM_V12].value = 12000000 * data / 192;

	cmd = ADM1025_Vcc;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMTM_Vcc].value = 3300000 * data / 192;

	if (sc->sc_model == 192) {
		cmd = SMSC47M192_V1_5;
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
			sc->sc_sensor[SMSC_V1_5].value = 1500000 * data / 192;

		cmd = SMSC47M192_V1_8;
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
			sc->sc_sensor[SMSC_V1_8].value = 1800000 * data / 192;

		cmd = SMSC47M192_TEMP2;
		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &sdata, sizeof sdata,
		    0) == 0)
			sc->sc_sensor[SMSC_TEMP2].value = 273150000 + 1000000 * sdata;

	}

	iic_release_bus(sc->sc_tag, 0);
}


code:
/*	$OpenBSD: adm1026.c,v 1.11 2008/06/09 16:21:33 cnst Exp $	*/

/*
 * Copyright (c) 2005 Theo de Raadt
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sensors.h>

#include <dev/i2c/i2cvar.h>

/* ADM 1026 registers */
#define ADM1026_TEMP		0x1f
#define ADM1026_STATUS		0x20
#define ADM1026_Vbat		0x26
#define ADM1026_Ain8		0x27
#define ADM1026_EXT1		0x28
#define ADM1026_EXT2		0x29
#define ADM1026_V3_3stby	0x2a
#define ADM1026_V3_3main	0x2b
#define ADM1026_V5		0x2c
#define ADM1026_Vccp		0x2d
#define ADM1026_V12		0x2e
#define ADM1026_Vminus12	0x2f
#define ADM1026_FAN0		0x38
#define ADM1026_FAN1		0x39
#define ADM1026_FAN2		0x3a
#define ADM1026_FAN3		0x3b
#define ADM1026_FAN4		0x3c
#define ADM1026_FAN5		0x3d
#define ADM1026_FAN6		0x3e
#define ADM1026_FAN7		0x3f
#define ADM1026_EXT1_OFF	0x6e
#define ADM1026_EXT2_OFF	0x6f
#define ADM1026_FAN0123DIV	0x02
#define ADM1026_FAN4567DIV	0x03
#define ADM1026_CONTROL		0x00
#define  ADM1026_CONTROL_START	0x01
#define  ADM1026_CONTROL_INTCLR	0x04

/* Sensors */
#define ADMCTS_TEMP		0
#define ADMCTS_EXT1		1
#define ADMCTS_EXT2		2
#define ADMCTS_Vbat		3
#define ADMCTS_V3_3stby		4
#define ADMCTS_V3_3main		5
#define ADMCTS_V5		6
#define ADMCTS_Vccp		7
#define ADMCTS_V12		8
#define ADMCTS_Vminus12		9
#define ADMCTS_FAN0		10
#define ADMCTS_FAN1		11
#define ADMCTS_FAN2		12
#define ADMCTS_FAN3		13
#define ADMCTS_FAN4		14
#define ADMCTS_FAN5		15
#define ADMCTS_FAN6		16
#define ADMCTS_FAN7		17
#define ADMCTS_NUM_SENSORS	18

struct admcts_softc {
	struct device	sc_dev;
	i2c_tag_t	sc_tag;
	i2c_addr_t	sc_addr;

	struct ksensor	sc_sensor[ADMCTS_NUM_SENSORS];
	struct ksensordev sc_sensordev;
	int		sc_fanmul[8];
};

int	admcts_match(struct device *, void *, void *);
void	admcts_attach(struct device *, struct device *, void *);
void	admcts_refresh(void *);

struct cfattach admcts_ca = {
	sizeof(struct admcts_softc), admcts_match, admcts_attach
};

struct cfdriver admcts_cd = {
	NULL, "admcts", DV_DULL
};

int
admcts_match(struct device *parent, void *match, void *aux)
{
	struct i2c_attach_args *ia = aux;

	if (strcmp(ia->ia_name, "adm1026") == 0)
		return (1);
	return (0);
}

void
admcts_attach(struct device *parent, struct device *self, void *aux)
{
	struct admcts_softc *sc = (struct admcts_softc *)self;
	struct i2c_attach_args *ia = aux;
	u_int8_t cmd, data, data2;
	int i;

	sc->sc_tag = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	iic_acquire_bus(sc->sc_tag, 0);
	cmd = ADM1026_CONTROL;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
		iic_release_bus(sc->sc_tag, 0);
		printf(": cannot get control register\n");
		return;
	}
	data2 = data | ADM1026_CONTROL_START;
	data2 = data2 & ~ADM1026_CONTROL_INTCLR;
	if (data != data2) {
		if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
		    sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data2, 0)) {
			iic_release_bus(sc->sc_tag, 0);
			printf(": cannot set control register\n");
			return;
		}
	}

	cmd = ADM1026_FAN0123DIV;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
		iic_release_bus(sc->sc_tag, 0);
		printf(": cannot get fan0123div register\n");
		return;
	}
	sc->sc_fanmul[0] = (1 << ((data >> 0) & 0x3));
	sc->sc_fanmul[1] = (1 << ((data >> 2) & 0x3));
	sc->sc_fanmul[2] = (1 << ((data >> 4) & 0x3));
	sc->sc_fanmul[3] = (1 << ((data >> 6) & 0x3));

	cmd = ADM1026_FAN4567DIV;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
		iic_release_bus(sc->sc_tag, 0);
		printf(": cannot get fan0123div register\n");
		return;
	}
	sc->sc_fanmul[4] = (1 << ((data >> 0) & 0x3));
	sc->sc_fanmul[5] = (1 << ((data >> 2) & 0x3));
	sc->sc_fanmul[6] = (1 << ((data >> 4) & 0x3));
	sc->sc_fanmul[7] = (1 << ((data >> 6) & 0x3));

	iic_release_bus(sc->sc_tag, 0);

	/* Initialize sensor data. */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));

	sc->sc_sensor[ADMCTS_TEMP].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ADMCTS_TEMP].desc, "Internal",
	    sizeof(sc->sc_sensor[ADMCTS_TEMP].desc));

	sc->sc_sensor[ADMCTS_Vbat].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADMCTS_Vbat].desc, "Vbat",
	    sizeof(sc->sc_sensor[ADMCTS_Vbat].desc));

	sc->sc_sensor[ADMCTS_EXT1].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ADMCTS_EXT1].desc, "External",
	    sizeof(sc->sc_sensor[ADMCTS_EXT1].desc));

	sc->sc_sensor[ADMCTS_EXT2].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ADMCTS_EXT2].desc, "External",
	    sizeof(sc->sc_sensor[ADMCTS_EXT2].desc));

	sc->sc_sensor[ADMCTS_V3_3stby].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADMCTS_V3_3stby].desc, "3.3 V standby",
	    sizeof(sc->sc_sensor[ADMCTS_V3_3stby].desc));

	sc->sc_sensor[ADMCTS_V3_3main].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADMCTS_V3_3main].desc, "3.3 V main",
	    sizeof(sc->sc_sensor[ADMCTS_V3_3main].desc));

	sc->sc_sensor[ADMCTS_V5].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADMCTS_V5].desc, "5 V",
	    sizeof(sc->sc_sensor[ADMCTS_V5].desc));

	sc->sc_sensor[ADMCTS_Vccp].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADMCTS_Vccp].desc, "Vccp",
	    sizeof(sc->sc_sensor[ADMCTS_Vccp].desc));

	sc->sc_sensor[ADMCTS_V12].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADMCTS_V12].desc, "12 V",
	    sizeof(sc->sc_sensor[ADMCTS_V12].desc));

	sc->sc_sensor[ADMCTS_Vminus12].type = SENSOR_VOLTS_DC;
	strlcpy(sc->sc_sensor[ADMCTS_Vminus12].desc, "-12 V",
	    sizeof(sc->sc_sensor[ADMCTS_Vminus12].desc));

	sc->sc_sensor[ADMCTS_FAN1].type = SENSOR_FANRPM;
	sc->sc_sensor[ADMCTS_FAN2].type = SENSOR_FANRPM;
	sc->sc_sensor[ADMCTS_FAN3].type = SENSOR_FANRPM;
	sc->sc_sensor[ADMCTS_FAN4].type = SENSOR_FANRPM;
	sc->sc_sensor[ADMCTS_FAN5].type = SENSOR_FANRPM;
	sc->sc_sensor[ADMCTS_FAN6].type = SENSOR_FANRPM;
	sc->sc_sensor[ADMCTS_FAN7].type = SENSOR_FANRPM;

	if (sensor_task_register(sc, admcts_refresh, 5) == NULL) {
		printf(", unable to register update task\n");
		return;
	}

	for (i = 0; i < ADMCTS_NUM_SENSORS; i++)
		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
	sensordev_install(&sc->sc_sensordev);

	printf("\n");
}

static void
fanval(struct ksensor *sens, int mul, u_int8_t data)
{
	int tmp = data * mul;

	if (tmp == 0)
		sens->flags |= SENSOR_FINVALID;
	else
		sens->value = 1630000 / tmp;
}

void
admcts_refresh(void *arg)
{
	struct admcts_softc *sc = arg;
	u_int8_t cmd, data;
	int8_t sdata;

	iic_acquire_bus(sc->sc_tag, 0);

	cmd = ADM1026_TEMP;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0)
		sc->sc_sensor[ADMCTS_TEMP].value = 273150000 + 1000000 * sdata;

	cmd = ADM1026_EXT1;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0)
		sc->sc_sensor[ADMCTS_EXT1].value = 273150000 + 1000000 * sdata;

	cmd = ADM1026_EXT2;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0)
		sc->sc_sensor[ADMCTS_EXT2].value = 273150000 + 1000000 * sdata;

	cmd = ADM1026_Vbat;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMCTS_Vbat].value = 3000000 * data / 192;

	cmd = ADM1026_V3_3stby;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMCTS_V3_3stby].value = 3300000 * data / 192;

	cmd = ADM1026_V3_3main;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMCTS_V3_3main].value = 3300000 * data / 192;

	cmd = ADM1026_V5;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMCTS_V5].value = 5500000 * data / 192;

	cmd = ADM1026_Vccp;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMCTS_Vccp].value = 2250000 * data / 192;

	cmd = ADM1026_V12;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMCTS_V12].value = 12000000 * data / 192;

	cmd = ADM1026_Vminus12;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMCTS_Vminus12].value = -2125000 * data / 192;

	cmd = ADM1026_FAN0;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		fanval(&sc->sc_sensor[ADMCTS_FAN0], sc->sc_fanmul[0], data);

	cmd = ADM1026_FAN1;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		fanval(&sc->sc_sensor[ADMCTS_FAN1], sc->sc_fanmul[1], data);

	cmd = ADM1026_FAN2;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		fanval(&sc->sc_sensor[ADMCTS_FAN2], sc->sc_fanmul[2], data);

	cmd = ADM1026_FAN3;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		fanval(&sc->sc_sensor[ADMCTS_FAN3], sc->sc_fanmul[3], data);

	cmd = ADM1026_FAN4;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		fanval(&sc->sc_sensor[ADMCTS_FAN4], sc->sc_fanmul[4], data);

	cmd = ADM1026_FAN5;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		fanval(&sc->sc_sensor[ADMCTS_FAN5], sc->sc_fanmul[5], data);

	cmd = ADM1026_FAN6;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		fanval(&sc->sc_sensor[ADMCTS_FAN6], sc->sc_fanmul[6], data);

	cmd = ADM1026_FAN7;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		fanval(&sc->sc_sensor[ADMCTS_FAN7], sc->sc_fanmul[7], data);

	iic_release_bus(sc->sc_tag, 0);
}


code:
/*	$OpenBSD: adm1030.c,v 1.9 2008/05/01 23:02:05 deraadt Exp $	*/

/*
 * Copyright (c) 2005 Theo de Raadt
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sensors.h>

#include <dev/i2c/i2cvar.h>

/* adm 1030 registers */
#define ADM1030_INT_TEMP	0x0a
#define ADM1030_EXT_TEMP	0x0b
#define ADM1030_FAN		0x08
#define ADM1030_FANC		0x20
#define  ADM1024_FANC_VAL(x)	(x >> 6)

/* Sensors */
#define ADMTMP_INT		0
#define ADMTMP_EXT		1
#define ADMTMP_FAN		2
#define ADMTMP_NUM_SENSORS	3

struct admtmp_softc {
	struct device	sc_dev;
	i2c_tag_t	sc_tag;
	i2c_addr_t	sc_addr;
	int		sc_fanmul;

	struct ksensor	sc_sensor[ADMTMP_NUM_SENSORS];
	struct ksensordev sc_sensordev;
};

int	admtmp_match(struct device *, void *, void *);
void	admtmp_attach(struct device *, struct device *, void *);
void	admtmp_refresh(void *);

struct cfattach admtmp_ca = {
	sizeof(struct admtmp_softc), admtmp_match, admtmp_attach
};

struct cfdriver admtmp_cd = {
	NULL, "admtmp", DV_DULL
};

int
admtmp_match(struct device *parent, void *match, void *aux)
{
	struct i2c_attach_args *ia = aux;

	if (strcmp(ia->ia_name, "adm1030") == 0)
		return (1);
	return (0);
}

void
admtmp_attach(struct device *parent, struct device *self, void *aux)
{
	struct admtmp_softc *sc = (struct admtmp_softc *)self;
	struct i2c_attach_args *ia = aux;
	u_int8_t cmd, data;
	int i;

	sc->sc_tag = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	cmd = ADM1030_FANC;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
		printf(": unable to read fan setting\n");
		return;
	}

	sc->sc_fanmul = 11250/8 * (1 << ADM1024_FANC_VAL(data)) * 60;

	/* Initialize sensor data. */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));

	sc->sc_sensor[ADMTMP_INT].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ADMTMP_INT].desc, "Internal",
	    sizeof(sc->sc_sensor[ADMTMP_INT].desc));

	sc->sc_sensor[ADMTMP_EXT].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ADMTMP_EXT].desc, "External",
	    sizeof(sc->sc_sensor[ADMTMP_EXT].desc));

	sc->sc_sensor[ADMTMP_FAN].type = SENSOR_FANRPM;

	if (sensor_task_register(sc, admtmp_refresh, 5) == NULL) {
		printf(": unable to register update task\n");
		return;
	}

	for (i = 0; i < ADMTMP_NUM_SENSORS; i++)
		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
	sensordev_install(&sc->sc_sensordev);

	printf("\n");
}

void
admtmp_refresh(void *arg)
{
	struct admtmp_softc *sc = arg;
	u_int8_t cmd, data;

	iic_acquire_bus(sc->sc_tag, 0);

	cmd = ADM1030_INT_TEMP;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMTMP_INT].value = 273150000 + 1000000 * data;

	cmd = ADM1030_EXT_TEMP;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMTMP_EXT].value = 273150000 + 1000000 * data;

	cmd = ADM1030_FAN;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0) {
		if (data == 0)
			sc->sc_sensor[ADMTMP_FAN].flags |= SENSOR_FINVALID;
		else {
			sc->sc_sensor[ADMTMP_FAN].value =
			    sc->sc_fanmul / (2 * (int)data);
			sc->sc_sensor[ADMTMP_FAN].flags &= ~SENSOR_FINVALID;
		}
	}

	iic_release_bus(sc->sc_tag, 0);
}


code:
/*	$OpenBSD: adm1031.c,v 1.8 2007/06/24 05:34:35 dlg Exp $	*/

/*
 * Copyright (c) 2005 Theo de Raadt
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sensors.h>

#include <dev/i2c/i2cvar.h>

/* adm 1031 registers */
#define ADM1031_INT_TEMP	0x0a
#define ADM1031_EXT_TEMP	0x0b
#define ADM1031_EXT2_TEMP	0x0c
#define ADM1031_FAN		0x08
#define ADM1031_FANC		0x20
#define ADM1031_FAN2		0x09
#define ADM1031_FAN2C		0x21
#define  ADM1024_FANC_VAL(x)	(x >> 6)

/* Sensors */
#define ADMTT_INT		0
#define ADMTT_EXT		1
#define ADMTT_EXT2		2
#define ADMTT_FAN		3
#define ADMTT_FAN2		4
#define ADMTT_NUM_SENSORS	5

struct admtt_softc {
	struct device	sc_dev;
	i2c_tag_t	sc_tag;
	i2c_addr_t	sc_addr;
	int		sc_fanmul;

	struct ksensor	sc_sensor[ADMTT_NUM_SENSORS];
	struct ksensordev sc_sensordev;
};

int	admtt_match(struct device *, void *, void *);
void	admtt_attach(struct device *, struct device *, void *);
void	admtt_refresh(void *);

struct cfattach admtt_ca = {
	sizeof(struct admtt_softc), admtt_match, admtt_attach
};

struct cfdriver admtt_cd = {
	NULL, "admtt", DV_DULL
};

int
admtt_match(struct device *parent, void *match, void *aux)
{
	struct i2c_attach_args *ia = aux;

	if (strcmp(ia->ia_name, "adm1031") == 0)
		return (1);
	return (0);
}

void
admtt_attach(struct device *parent, struct device *self, void *aux)
{
	struct admtt_softc *sc = (struct admtt_softc *)self;
	struct i2c_attach_args *ia = aux;
	u_int8_t cmd, data;
	int i;

	sc->sc_tag = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	cmd = ADM1031_FANC;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
		printf(", unable to read fan setting\n");
		return;
	}
	sc->sc_fanmul = 11250/8 * (1 << ADM1024_FANC_VAL(data)) * 60;

	/* Initialize sensor data. */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));

	sc->sc_sensor[ADMTT_INT].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ADMTT_INT].desc, "Internal",
	    sizeof(sc->sc_sensor[ADMTT_INT].desc));

	sc->sc_sensor[ADMTT_EXT].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ADMTT_EXT].desc, "External",
	    sizeof(sc->sc_sensor[ADMTT_EXT].desc));

	sc->sc_sensor[ADMTT_EXT2].type = SENSOR_TEMP;
	strlcpy(sc->sc_sensor[ADMTT_EXT2].desc, "External",
	    sizeof(sc->sc_sensor[ADMTT_EXT2].desc));

	sc->sc_sensor[ADMTT_FAN].type = SENSOR_FANRPM;

	sc->sc_sensor[ADMTT_FAN2].type = SENSOR_FANRPM;

	if (sensor_task_register(sc, admtt_refresh, 5) == NULL) {
		printf(", unable to register update task\n");
		return;
	}

	for (i = 0; i < ADMTT_NUM_SENSORS; i++)
		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
	sensordev_install(&sc->sc_sensordev);

	printf("\n");
}

void
admtt_refresh(void *arg)
{
	struct admtt_softc *sc = arg;
	u_int8_t cmd, data;

	iic_acquire_bus(sc->sc_tag, 0);

	cmd = ADM1031_INT_TEMP;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMTT_INT].value = 273150000 + 1000000 * data;

	cmd = ADM1031_EXT_TEMP;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMTT_EXT].value = 273150000 + 1000000 * data;

	cmd = ADM1031_EXT2_TEMP;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0)
		sc->sc_sensor[ADMTT_EXT2].value = 273150000 + 1000000 * data;

	cmd = ADM1031_FAN;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0) {
		if (data == 0)
			sc->sc_sensor[ADMTT_FAN].flags |= SENSOR_FINVALID;
		else {
			sc->sc_sensor[ADMTT_FAN].value =
			    sc->sc_fanmul / (2 * (int)data);
			sc->sc_sensor[ADMTT_FAN].flags &= ~SENSOR_FINVALID;
		}
	}

	cmd = ADM1031_FAN2;
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0) == 0) {
		if (data == 0)
			sc->sc_sensor[ADMTT_FAN2].flags |= SENSOR_FINVALID;
		else {
			sc->sc_sensor[ADMTT_FAN2].value =
			    sc->sc_fanmul / (2 * (int)data);
			sc->sc_sensor[ADMTT_FAN2].flags &= ~SENSOR_FINVALID;
		}
	}

	iic_release_bus(sc->sc_tag, 0);
}