キースキャン処理

んー、もしかして row と column って逆なのかな。チャージの意味も逆かもしれないなぁ。
バグがあったので修正。

typedef int key_scan_code_t;

#define	KS_POWER	0xad		/* Power key */

#define	CS2_ADDRESS	0x08000000

#define	KBDCOL_L	(CS2_ADDRESS + 0x00)	/* Write */
#define	KBDCOL_U	(CS2_ADDRESS + 0x04)	/* Write */
#define	KBDCHARGE	(CS2_ADDRESS + 0x08)	/* Write */
#define	KBDDATA		(CS2_ADDRESS + 0x08)	/* Read */

#define	MAXPRESS	10	/* キー同時最大押下数 */
#define	NCOLUMN 	12	/* キーマトリックス桁数 */
#define	NROW		7	/* キーマトリックス行数 */
#define	KEYWAIT		10	/* wait (ms?) (OS Timer のカウンタで KEYWAIT * 4000 分待つ) */

/*
 * キースキャンコードテーブル
 *
 * 0x30d0-0x30e8: copy 8192 * 16 (=128Ki) byte from address 0(ROM) to 0xa00a0000(SDRAM).
 * キースキャンコードのテーブルは SDRAM 上では 0xa00a1758 に存在するので、
 * ROM 上のアドレスに変換すると 0x1758 になる。
 */
static const key_scan_code_t key_scan_code_tbl[NCOLUMN][NROW] = {
	{ 0x84, 0x31, 0x33, 0x35, 0x36, 0x37, 0x39, },
	{ 0x30, 0x08, 0x00, 0xdb, 0xd6, 0x00, 0x32, },
	{ 0x34, 0x52, 0x59, 0x38, 0x49, 0x4f, 0x50, },
	{ 0x00, 0xd8, 0xd7, 0x09, 0x51, 0x45, 0x54, },
	{ 0x47, 0x55, 0x4a, 0x4b, 0x00, 0x00, 0x00, },
	{ 0x00, 0x00, 0x57, 0x53, 0x46, 0x56, 0x48, },
	{ 0x4d, 0x4c, 0x00, 0x00, 0xd5, 0x00, 0xde, },
	{ 0x41, 0x44, 0x43, 0x42, 0x4e, 0x2e, 0x00, },
	{ 0x0d, 0x00, 0xdc, 0x00, 0xab, 0x5a, 0x58, },
	{ 0x2d, 0x20, 0x2f, 0x00, 0x8b, 0x00, 0x00, },
	{ 0xd9, 0x85, 0xdd, 0xcf, 0x00, 0xa7, 0xa8, },
	{ 0x2c, 0x8c, 0x8a, 0x8d, 0x00, 0xda, 0x00, },
};

/**
 * \brief
 * 押下キーを検出して、キーバッファに押下キースキャンコードを格納する。
 *
 * キーマトリックスを走査し押下キーを検出して、keybuf に押下キーの
 * スキャンコードを格納する。<br>
 * また、キーマトリックス走査の他に GPIO<95> のレベルを取得し Hi レベルで
 * あれば電源キーのスキャンコードを keybuf に格納する。<br>
 * 押下キーが nkeybuf に指定された値、もしくはコンパイル時に指定された値
 * (MAXPRESS) よりも多く検出された場合、キー押下は無かったものとして扱われる。
 *
 * \param nkeybuf キーバッファ数
 * \param keybuf キースキャンコードを格納するメモリ領域へのポインタ。
 *               呼び出し側で nkeybuf 個数分のメモリを確保しなければならない。
 * \return 押下キー数
 */
int
keyscan(int nkeybuf, key_scan_code_t *keybuf)
{
	struct {
		int col;
		int row;
	} key[MAXPRESS];
	key_scan_code_t *keybufp;
	int row, col;
	int npress;
	int data;
	int powerkey;
	int i;

	_ASSERT(0 < nkeybuf);
	_ASSERT(keybuf != NULL);

	if (nkeybuf > MAXPRESS)
		nkeybuf = MAXPRESS;

	/* get GPIO<95> level sense (maybe check power-key pressed...) */
	powerkey = GPIO_GPLR(95);

	npress = 0;
	for (col = 0; col < NCOLUMN; col++) {
		/* deselect column# and charge */
		CSR_WRITE1(KBDCOL_L, 0);
		CSR_WRITE1(KBDCOL_U, 0);
		CSR_WRITE1(KBDCHARGE, 1);
		delay(KEYWAIT);

		/* discharge & select scan column# */
		CSR_WRITE1(KBDCHARGE, 0);
		if (col < 8/*NCHAR_BIT*/) {
			CSR_WRITE1(KBDCOL_L, 1U << col);
			CSR_WRITE1(KBDCOL_U, 0);
		} else {
			CSR_WRITE1(KBDCOL_L, 0);
			CSR_WRITE1(KBDCOL_U, 1U << (col - 8));
		}
		delay(KEYWAIT);

		for (row = 0; row < NROW; row++) {
			CSR_WRITE1(KBDCHARGE, 0);
			data = CSR_READ1(KBDDATA);
			if ((data >> row) & 1) {
				/* detect key press */
				if (npress >= MAXPRESS) {
					npress = 0;
					goto nokeypress;
				}
				key[npress].col = col;
				key[npress].row = row;
				npress++;
			}
		}
	}
	if (npress >= nkeybuf) {
		npress = 0;
		goto nokeypress;
	}

	keybufp = keybuf;
	if (powerkey) {
		if (npress + 1 >= nkeybuf) {
			npress = 0;
			goto nokeypress;
		}
		keybufp++;
	}
	/* column/row -> key scan code */
	for (i = 0; i < npress; i++) {
		keybufp[i] = key_scan_code_tbl[key[i].col][key[i].row];
	}
	if (powerkey) {
		keybuf[0] = KS_POWER;
		npress++;
	}

nokeypress:
	/* deselect column# and charge */
	CSR_WRITE1(KBDCOL_L, 0);
	CSR_WRITE1(KBDCOL_U, 0);
	CSR_WRITE1(KBDCHARGE, 1);
	delay(KEYWAIT);

	/* discharge for power management */
	CSR_WRITE1(KBDCHARGE, 0);

	return npress;
}