⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hil_mlc.c

📁 QQ2440板子
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * HIL MLC state machine and serio interface driver * * Copyright (c) 2001 Brian S. Julin * 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, *    without modification. * 2. The name of the author may not be used to endorse or promote products *    derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL"). * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 * * References: * HP-HIL Technical Reference Manual.  Hewlett Packard Product No. 45918A * * *	Driver theory of operation: * *	Some access methods and an ISR is defined by the sub-driver  *	(e.g. hp_sdc_mlc.c).  These methods are expected to provide a  *	few bits of logic in addition to raw access to the HIL MLC,  *	specifically, the ISR, which is entirely registered by the  *	sub-driver and invoked directly, must check for record  *	termination or packet match, at which point a semaphore must *	be cleared and then the hil_mlcs_tasklet must be scheduled. * *	The hil_mlcs_tasklet processes the state machine for all MLCs *	each time it runs, checking each MLC's progress at the current *	node in the state machine, and moving the MLC to subsequent nodes *	in the state machine when appropriate.  It will reschedule *	itself if output is pending.  (This rescheduling should be replaced *	at some point with a sub-driver-specific mechanism.) * *	A timer task prods the tasklet once per second to prevent  *	hangups when attached devices do not return expected data *	and to initiate probes of the loop for new devices. */#include <linux/hil_mlc.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/timer.h>#include <linux/sched.h>#include <linux/list.h>MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");MODULE_DESCRIPTION("HIL MLC serio");MODULE_LICENSE("Dual BSD/GPL");EXPORT_SYMBOL(hil_mlc_register);EXPORT_SYMBOL(hil_mlc_unregister);#define PREFIX "HIL MLC: "static LIST_HEAD(hil_mlcs);static DEFINE_RWLOCK(hil_mlcs_lock);static struct timer_list	hil_mlcs_kicker;static int			hil_mlcs_probe;static void hil_mlcs_process(unsigned long unused);DECLARE_TASKLET_DISABLED(hil_mlcs_tasklet, hil_mlcs_process, 0);/* #define HIL_MLC_DEBUG *//********************** Device info/instance management **********************/static void hil_mlc_clear_di_map (hil_mlc *mlc, int val) {	int j;	for (j = val; j < 7 ; j++) {		mlc->di_map[j] = -1;	}}static void hil_mlc_clear_di_scratch (hil_mlc *mlc) {	memset(&(mlc->di_scratch), 0, sizeof(mlc->di_scratch));}static void hil_mlc_copy_di_scratch (hil_mlc *mlc, int idx) {	memcpy(&(mlc->di[idx]), &(mlc->di_scratch), sizeof(mlc->di_scratch));}static int hil_mlc_match_di_scratch (hil_mlc *mlc) {	int idx;	for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {		int j, found;		/* In-use slots are not eligible. */		found = 0;		for (j = 0; j < 7 ; j++) {			if (mlc->di_map[j] == idx) found++;		}		if (found) continue;		if (!memcmp(mlc->di + idx, 			    &(mlc->di_scratch), 			    sizeof(mlc->di_scratch))) break;	}	return((idx >= HIL_MLC_DEVMEM) ? -1 : idx);}static int hil_mlc_find_free_di(hil_mlc *mlc) {	int idx;	/* TODO: Pick all-zero slots first, failing that, 	 * randomize the slot picked among those eligible. 	 */	for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {		int j, found;		found = 0;		for (j = 0; j < 7 ; j++) {			if (mlc->di_map[j] == idx) found++;		}		if (!found) break;	}	return(idx); /* Note: It is guaranteed at least one above will match */}static inline void hil_mlc_clean_serio_map(hil_mlc *mlc) {	int idx;	for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {		int j, found;		found = 0;		for (j = 0; j < 7 ; j++) {			if (mlc->di_map[j] == idx) found++;		}		if (!found) mlc->serio_map[idx].di_revmap = -1;	}}static void hil_mlc_send_polls(hil_mlc *mlc) {	int did, i, cnt;	struct serio *serio;	struct serio_driver *drv;	i = cnt = 0;	did = (mlc->ipacket[0] & HIL_PKT_ADDR_MASK) >> 8;	serio = did ? mlc->serio[mlc->di_map[did - 1]] : NULL;	drv = (serio != NULL) ? serio->drv : NULL;	while (mlc->icount < 15 - i) {		hil_packet p;		p = mlc->ipacket[i];		if (did != (p & HIL_PKT_ADDR_MASK) >> 8) {			if (drv == NULL || drv->interrupt == NULL) goto skip;			drv->interrupt(serio, 0, 0, NULL);			drv->interrupt(serio, HIL_ERR_INT >> 16, 0, NULL);			drv->interrupt(serio, HIL_PKT_CMD >> 8,  0, NULL);			drv->interrupt(serio, HIL_CMD_POL + cnt, 0, NULL);		skip:			did = (p & HIL_PKT_ADDR_MASK) >> 8;			serio = did ? mlc->serio[mlc->di_map[did-1]] : NULL;			drv = (serio != NULL) ? serio->drv : NULL;			cnt = 0;		}		cnt++; i++;		if (drv == NULL || drv->interrupt == NULL) continue;		drv->interrupt(serio, (p >> 24), 0, NULL);		drv->interrupt(serio, (p >> 16) & 0xff, 0, NULL);		drv->interrupt(serio, (p >> 8) & ~HIL_PKT_ADDR_MASK, 0, NULL);		drv->interrupt(serio, p & 0xff, 0, NULL);	}}/*************************** State engine *********************************/#define HILSEN_SCHED	0x000100	/* Schedule the tasklet		*/#define HILSEN_BREAK	0x000200	/* Wait until next pass		*/#define HILSEN_UP	0x000400	/* relative node#, decrement	*/#define HILSEN_DOWN	0x000800	/* relative node#, increment	*/#define HILSEN_FOLLOW	0x001000	/* use retval as next node#	*/#define HILSEN_MASK	0x0000ff#define HILSEN_START	0#define HILSEN_RESTART	1#define HILSEN_DHR	9#define HILSEN_DHR2	10#define HILSEN_IFC	14#define HILSEN_HEAL0	16#define HILSEN_HEAL	18#define HILSEN_ACF      21#define HILSEN_ACF2	22#define HILSEN_DISC0	25#define HILSEN_DISC	27#define HILSEN_MATCH	40#define HILSEN_OPERATE	41#define HILSEN_PROBE	44#define HILSEN_DSR	52#define HILSEN_REPOLL	55#define HILSEN_IFCACF	58#define HILSEN_END	60#define HILSEN_NEXT	(HILSEN_DOWN | 1)#define HILSEN_SAME	(HILSEN_DOWN | 0)#define HILSEN_LAST	(HILSEN_UP | 1)#define HILSEN_DOZE	(HILSEN_SAME | HILSEN_SCHED | HILSEN_BREAK)#define HILSEN_SLEEP	(HILSEN_SAME | HILSEN_BREAK)static int hilse_match(hil_mlc *mlc, int unused) {	int rc;	rc = hil_mlc_match_di_scratch(mlc);	if (rc == -1) {		rc = hil_mlc_find_free_di(mlc);		if (rc == -1) goto err;#ifdef HIL_MLC_DEBUG		printk(KERN_DEBUG PREFIX "new in slot %i\n", rc);#endif		hil_mlc_copy_di_scratch(mlc, rc);		mlc->di_map[mlc->ddi] = rc;		mlc->serio_map[rc].di_revmap = mlc->ddi;		hil_mlc_clean_serio_map(mlc);		serio_rescan(mlc->serio[rc]);		return -1;	}	mlc->di_map[mlc->ddi] = rc;#ifdef HIL_MLC_DEBUG	printk(KERN_DEBUG PREFIX "same in slot %i\n", rc);#endif	mlc->serio_map[rc].di_revmap = mlc->ddi;	hil_mlc_clean_serio_map(mlc);	return 0; err:	printk(KERN_ERR PREFIX "Residual device slots exhausted, close some serios!\n");	return 1;}/* An LCV used to prevent runaway loops, forces 5 second sleep when reset. */static int hilse_init_lcv(hil_mlc *mlc, int unused) {	struct timeval tv;	do_gettimeofday(&tv);	if(mlc->lcv == 0) goto restart;  /* First init, no need to dally */	if(tv.tv_sec - mlc->lcv_tv.tv_sec < 5) return -1; restart:	mlc->lcv_tv = tv;	mlc->lcv = 0;	return 0;}static int hilse_inc_lcv(hil_mlc *mlc, int lim) {	if (mlc->lcv++ >= lim) return -1;	return 0;}#if 0static int hilse_set_lcv(hil_mlc *mlc, int val) {	mlc->lcv = val;	return 0;}#endif/* Management of the discovered device index (zero based, -1 means no devs) */static int hilse_set_ddi(hil_mlc *mlc, int val) {	mlc->ddi = val;	hil_mlc_clear_di_map(mlc, val + 1);	return 0;}static int hilse_dec_ddi(hil_mlc *mlc, int unused) {	mlc->ddi--;	if (mlc->ddi <= -1) { 		mlc->ddi = -1;		hil_mlc_clear_di_map(mlc, 0);		return -1;	}	hil_mlc_clear_di_map(mlc, mlc->ddi + 1);	return 0;}static int hilse_inc_ddi(hil_mlc *mlc, int unused) {	if (mlc->ddi >= 6) {		BUG();		return -1;	}	mlc->ddi++;	return 0;}static int hilse_take_idd(hil_mlc *mlc, int unused) {	int i;	/* Help the state engine: 	 * Is this a real IDD response or just an echo? 	 *	 * Real IDD response does not start with a command. 	 */	if (mlc->ipacket[0] & HIL_PKT_CMD) goto bail;	/* Should have the command echoed further down. */	for (i = 1; i < 16; i++) {		if (((mlc->ipacket[i] & HIL_PKT_ADDR_MASK) == 		     (mlc->ipacket[0] & HIL_PKT_ADDR_MASK)) &&		    (mlc->ipacket[i] & HIL_PKT_CMD) && 		    ((mlc->ipacket[i] & HIL_PKT_DATA_MASK) == HIL_CMD_IDD))			break;	}	if (i > 15) goto bail;	/* And the rest of the packets should still be clear. */	while (++i < 16) {		if (mlc->ipacket[i]) break;	}	if (i < 16) goto bail;	for (i = 0; i < 16; i++) {		mlc->di_scratch.idd[i] = 			mlc->ipacket[i] & HIL_PKT_DATA_MASK;	}	/* Next step is to see if RSC supported */	if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_RSC) 		return HILSEN_NEXT;	if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD) 		return HILSEN_DOWN | 4;	return 0; bail:	mlc->ddi--;	return -1; /* This should send us off to ACF */}static int hilse_take_rsc(hil_mlc *mlc, int unused) {	int i;	for (i = 0; i < 16; i++) {		mlc->di_scratch.rsc[i] = 			mlc->ipacket[i] & HIL_PKT_DATA_MASK;	}	/* Next step is to see if EXD supported (IDD has already been read) */	if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD) 		return HILSEN_NEXT;	return 0;}static int hilse_take_exd(hil_mlc *mlc, int unused) {	int i;	for (i = 0; i < 16; i++) {		mlc->di_scratch.exd[i] = 			mlc->ipacket[i] & HIL_PKT_DATA_MASK;	}	/* Next step is to see if RNM supported. */	if (mlc->di_scratch.exd[0] & HIL_EXD_HEADER_RNM) 		return HILSEN_NEXT;	return 0;}static int hilse_take_rnm(hil_mlc *mlc, int unused) {	int i;	for (i = 0; i < 16; i++) {		mlc->di_scratch.rnm[i] = 			mlc->ipacket[i] & HIL_PKT_DATA_MASK;	}	do {	  char nam[17];	  snprintf(nam, 16, "%s", mlc->di_scratch.rnm);	  nam[16] = '\0';	  printk(KERN_INFO PREFIX "Device name gotten: %s\n", nam);	} while (0);	return 0;}static int hilse_operate(hil_mlc *mlc, int repoll) { 	if (mlc->opercnt == 0) hil_mlcs_probe = 0;	mlc->opercnt = 1;	hil_mlc_send_polls(mlc);	if (!hil_mlcs_probe) return 0;	hil_mlcs_probe = 0;	mlc->opercnt = 0;	return 1;}#define FUNC(funct, funct_arg, zero_rc, neg_rc, pos_rc) \{ HILSE_FUNC,		{ func: &funct }, funct_arg, zero_rc, neg_rc, pos_rc },#define OUT(pack) \{ HILSE_OUT,		{ packet: pack }, 0, HILSEN_NEXT, HILSEN_DOZE, 0 },#define CTS \{ HILSE_CTS,		{ packet: 0    }, 0, HILSEN_NEXT | HILSEN_SCHED | HILSEN_BREAK, HILSEN_DOZE, 0 },#define EXPECT(comp, to, got, got_wrong, timed_out) \{ HILSE_EXPECT,		{ packet: comp }, to, got, got_wrong, timed_out },#define EXPECT_LAST(comp, to, got, got_wrong, timed_out) \{ HILSE_EXPECT_LAST,	{ packet: comp }, to, got, got_wrong, timed_out },#define EXPECT_DISC(comp, to, got, got_wrong, timed_out) \{ HILSE_EXPECT_DISC,	{ packet: comp }, to, got, got_wrong, timed_out },#define IN(to, got, got_error, timed_out) \{ HILSE_IN,		{ packet: 0    }, to, got, got_error, timed_out },#define OUT_DISC(pack) \{ HILSE_OUT_DISC,	{ packet: pack }, 0, 0, 0, 0 },#define OUT_LAST(pack) \{ HILSE_OUT_LAST,	{ packet: pack }, 0, 0, 0, 0 },struct hilse_node hil_mlc_se[HILSEN_END] = {	/* 0  HILSEN_START */	FUNC(hilse_init_lcv, 0,	HILSEN_NEXT,	HILSEN_SLEEP,	0)	/* 1  HILSEN_RESTART */	FUNC(hilse_inc_lcv, 10,	HILSEN_NEXT,	HILSEN_START,  0)	OUT(HIL_CTRL_ONLY)			/* Disable APE */	CTS#define TEST_PACKET(x) \(HIL_PKT_CMD | (x << HIL_PKT_ADDR_SHIFT) | x << 4 | x)	OUT(HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET(0x5))	EXPECT(HIL_ERR_INT | TEST_PACKET(0x5),	       2000,		HILSEN_NEXT,	HILSEN_RESTART,	HILSEN_RESTART)	OUT(HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET(0xa))	EXPECT(HIL_ERR_INT | TEST_PACKET(0xa),	       2000,		HILSEN_NEXT,	HILSEN_RESTART,	HILSEN_RESTART)	OUT(HIL_CTRL_ONLY | 0)			/* Disable test mode */		/* 9  HILSEN_DHR */	FUNC(hilse_init_lcv, 0,	HILSEN_NEXT,	HILSEN_SLEEP,	0)	/* 10 HILSEN_DHR2 */	FUNC(hilse_inc_lcv, 10,	HILSEN_NEXT,	HILSEN_START,	0)	FUNC(hilse_set_ddi, -1,	HILSEN_NEXT,	0,		0)	OUT(HIL_PKT_CMD | HIL_CMD_DHR)	IN(300000,		HILSEN_DHR2,	HILSEN_DHR2,	HILSEN_NEXT)	/* 14 HILSEN_IFC */  	OUT(HIL_PKT_CMD | HIL_CMD_IFC)	EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT,	       20000,		HILSEN_DISC,	HILSEN_DHR2,	HILSEN_NEXT )	/* If devices are there, they weren't in PUP or other loopback mode.	 * We're more concerned at this point with restoring operation	 * to devices than discovering new ones, so we try to salvage	 * the loop configuration by closing off the loop.	 */	/* 16 HILSEN_HEAL0 */	FUNC(hilse_dec_ddi, 0,	HILSEN_NEXT,	HILSEN_ACF,	0)	FUNC(hilse_inc_ddi, 0,	HILSEN_NEXT,	0,		0)	/* 18 HILSEN_HEAL */	OUT_LAST(HIL_CMD_ELB)	EXPECT_LAST(HIL_CMD_ELB | HIL_ERR_INT, 		    20000,	HILSEN_REPOLL,	HILSEN_DSR,	HILSEN_NEXT)	FUNC(hilse_dec_ddi, 0,	HILSEN_HEAL,	HILSEN_NEXT,	0)	/* 21 HILSEN_ACF */	FUNC(hilse_init_lcv, 0,	HILSEN_NEXT,	HILSEN_DOZE,	0)	/* 22 HILSEN_ACF2 */	FUNC(hilse_inc_lcv, 10,	HILSEN_NEXT,	HILSEN_START,	0)	OUT(HIL_PKT_CMD | HIL_CMD_ACF | 1)	IN(20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_NEXT)	/* 25 HILSEN_DISC0 */	OUT_DISC(HIL_PKT_CMD | HIL_CMD_ELB)	EXPECT_DISC(HIL_PKT_CMD | HIL_CMD_ELB | HIL_ERR_INT,	       20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_DSR)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -