1 /*
2  * HIL MLC state machine and serio interface driver
3  *
4  * Copyright (c) 2001 Brian S. Julin
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions, and the following disclaimer,
12  *    without modification.
13  * 2. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * Alternatively, this software may be distributed under the terms of the
17  * GNU General Public License ("GPL").
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  *
29  * References:
30  * HP-HIL Technical Reference Manual.  Hewlett Packard Product No. 45918A
31  *
32  *
33  *	Driver theory of operation:
34  *
35  *	Some access methods and an ISR is defined by the sub-driver
36  *	(e.g. hp_sdc_mlc.c).  These methods are expected to provide a
37  *	few bits of logic in addition to raw access to the HIL MLC,
38  *	specifically, the ISR, which is entirely registered by the
39  *	sub-driver and invoked directly, must check for record
40  *	termination or packet match, at which point a semaphore must
41  *	be cleared and then the hil_mlcs_tasklet must be scheduled.
42  *
43  *	The hil_mlcs_tasklet processes the state machine for all MLCs
44  *	each time it runs, checking each MLC's progress at the current
45  *	node in the state machine, and moving the MLC to subsequent nodes
46  *	in the state machine when appropriate.  It will reschedule
47  *	itself if output is pending.  (This rescheduling should be replaced
48  *	at some point with a sub-driver-specific mechanism.)
49  *
50  *	A timer task prods the tasket once per second to prevent
51  *	hangups when attached devices do not return expected data
52  *	and to initiate probes of the loop for new devices.
53  */
54 
55 #include <linux/hil_mlc.h>
56 #include <linux/errno.h>
57 #include <linux/kernel.h>
58 #include <linux/module.h>
59 #include <linux/init.h>
60 #include <linux/interrupt.h>
61 #include <linux/timer.h>
62 #include <linux/sched.h>
63 #include <linux/list.h>
64 
65 MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
66 MODULE_DESCRIPTION("HIL MLC serio");
67 MODULE_LICENSE("Dual BSD/GPL");
68 
69 EXPORT_SYMBOL(hil_mlc_register);
70 EXPORT_SYMBOL(hil_mlc_unregister);
71 
72 #define PREFIX "HIL MLC: "
73 
74 static LIST_HEAD(hil_mlcs);
75 static rwlock_t			hil_mlcs_lock = RW_LOCK_UNLOCKED;
76 static struct timer_list	hil_mlcs_kicker;
77 static int			hil_mlcs_probe;
78 
79 static void hil_mlcs_process(unsigned long unused);
80 DECLARE_TASKLET_DISABLED(hil_mlcs_tasklet, hil_mlcs_process, 0);
81 
82 
83 /* #define HIL_MLC_DEBUG */
84 
85 /********************** Device info/instance management **********************/
86 
hil_mlc_clear_di_map(hil_mlc * mlc,int val)87 static void hil_mlc_clear_di_map (hil_mlc *mlc, int val) {
88 	int j;
89 	for (j = val; j < 7 ; j++) {
90 		mlc->di_map[j] = -1;
91 	}
92 }
93 
hil_mlc_clear_di_scratch(hil_mlc * mlc)94 static void hil_mlc_clear_di_scratch (hil_mlc *mlc) {
95 	memset(&(mlc->di_scratch), 0, sizeof(mlc->di_scratch));
96 }
97 
hil_mlc_copy_di_scratch(hil_mlc * mlc,int idx)98 static void hil_mlc_copy_di_scratch (hil_mlc *mlc, int idx) {
99 	memcpy(&(mlc->di[idx]), &(mlc->di_scratch), sizeof(mlc->di_scratch));
100 }
101 
hil_mlc_match_di_scratch(hil_mlc * mlc)102 static int hil_mlc_match_di_scratch (hil_mlc *mlc) {
103 	int idx;
104 
105 	for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {
106 		int j, found;
107 
108 		/* In-use slots are not eligible. */
109 		found = 0;
110 		for (j = 0; j < 7 ; j++) {
111 			if (mlc->di_map[j] == idx) found++;
112 		}
113 		if (found) continue;
114 		if (!memcmp(mlc->di + idx,
115 			    &(mlc->di_scratch),
116 			    sizeof(mlc->di_scratch))) break;
117 	}
118 	return((idx >= HIL_MLC_DEVMEM) ? -1 : idx);
119 }
120 
hil_mlc_find_free_di(hil_mlc * mlc)121 static int hil_mlc_find_free_di(hil_mlc *mlc) {
122 	int idx;
123 	/* TODO: Pick all-zero slots first, failing that,
124 	 * randomize the slot picked among those eligible.
125 	 */
126 	for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {
127 		int j, found;
128 		found = 0;
129 		for (j = 0; j < 7 ; j++) {
130 			if (mlc->di_map[j] == idx) found++;
131 		}
132 		if (!found) break;
133 	}
134 	return(idx); /* Note: It is guaranteed at least one above will match */
135 }
136 
hil_mlc_clean_serio_map(hil_mlc * mlc)137 static inline void hil_mlc_clean_serio_map(hil_mlc *mlc) {
138 	int idx;
139 	for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {
140 		int j, found;
141 		found = 0;
142 		for (j = 0; j < 7 ; j++) {
143 			if (mlc->di_map[j] == idx) found++;
144 		}
145 		if (!found) mlc->serio_map[idx].di_revmap = -1;
146 	}
147 }
148 
hil_mlc_send_polls(hil_mlc * mlc)149 static void hil_mlc_send_polls(hil_mlc *mlc) {
150 	int did, i, cnt;
151 	struct serio *serio;
152 	struct serio_dev *dev;
153 
154 	i = cnt = 0;
155 	did = (mlc->ipacket[0] & HIL_PKT_ADDR_MASK) >> 8;
156 	serio = did ? &(mlc->serio[mlc->di_map[did - 1]]) : NULL;
157 	dev = (serio != NULL) ? serio->dev : NULL;
158 
159 	while (mlc->icount < 15 - i) {
160 		hil_packet p;
161 		p = mlc->ipacket[i];
162 		if (did != (p & HIL_PKT_ADDR_MASK) >> 8) {
163 			if (dev == NULL || dev->interrupt == NULL) goto skip;
164 
165 			dev->interrupt(serio, 0, 0);
166 			dev->interrupt(serio, HIL_ERR_INT >> 16, 0);
167 			dev->interrupt(serio, HIL_PKT_CMD >> 8, 0);
168 			dev->interrupt(serio, HIL_CMD_POL + cnt, 0);
169 		skip:
170 			did = (p & HIL_PKT_ADDR_MASK) >> 8;
171 			serio = did ? &(mlc->serio[mlc->di_map[did-1]]) : NULL;
172 			dev = (serio != NULL) ? serio->dev : NULL;
173 			cnt = 0;
174 		}
175 		cnt++; i++;
176 		if (dev == NULL || dev->interrupt == NULL) continue;
177 		dev->interrupt(serio, (p >> 24), 0);
178 		dev->interrupt(serio, (p >> 16) & 0xff, 0);
179 		dev->interrupt(serio, (p >> 8) & ~HIL_PKT_ADDR_MASK, 0);
180 		dev->interrupt(serio, p & 0xff, 0);
181 	}
182 }
183 
184 /*************************** State engine *********************************/
185 
186 #define HILSEN_SCHED	0x000100	/* Schedule the tasklet		*/
187 #define HILSEN_BREAK	0x000200	/* Wait until next pass		*/
188 #define HILSEN_UP	0x000400	/* relative node#, decrement	*/
189 #define HILSEN_DOWN	0x000800	/* relative node#, increment	*/
190 #define HILSEN_FOLLOW	0x001000	/* use retval as next node#	*/
191 
192 #define HILSEN_MASK	0x0000ff
193 #define HILSEN_START	0
194 #define HILSEN_RESTART	1
195 #define HILSEN_DHR	9
196 #define HILSEN_DHR2	10
197 #define HILSEN_IFC	14
198 #define HILSEN_HEAL0	16
199 #define HILSEN_HEAL	18
200 #define HILSEN_ACF      21
201 #define HILSEN_ACF2	22
202 #define HILSEN_DISC0	25
203 #define HILSEN_DISC	27
204 #define HILSEN_MATCH	40
205 #define HILSEN_OPERATE	41
206 #define HILSEN_PROBE	44
207 #define HILSEN_DSR	52
208 #define HILSEN_REPOLL	55
209 #define HILSEN_IFCACF	58
210 #define HILSEN_END	60
211 
212 #define HILSEN_NEXT	(HILSEN_DOWN | 1)
213 #define HILSEN_SAME	(HILSEN_DOWN | 0)
214 #define HILSEN_LAST	(HILSEN_UP | 1)
215 
216 #define HILSEN_DOZE	(HILSEN_SAME | HILSEN_SCHED | HILSEN_BREAK)
217 #define HILSEN_SLEEP	(HILSEN_SAME | HILSEN_BREAK)
218 
hilse_match(hil_mlc * mlc,int unused)219 static int hilse_match(hil_mlc *mlc, int unused) {
220 	int rc;
221 	rc = hil_mlc_match_di_scratch(mlc);
222 	if (rc == -1) {
223 		rc = hil_mlc_find_free_di(mlc);
224 		if (rc == -1) goto err;
225 #ifdef HIL_MLC_DEBUG
226 		printk(KERN_DEBUG PREFIX "new in slot %i\n", rc);
227 #endif
228 		hil_mlc_copy_di_scratch(mlc, rc);
229 		mlc->di_map[mlc->ddi] = rc;
230 		mlc->serio_map[rc].di_revmap = mlc->ddi;
231 		hil_mlc_clean_serio_map(mlc);
232 		serio_rescan(mlc->serio + rc);
233 		return -1;
234 	}
235 	mlc->di_map[mlc->ddi] = rc;
236 #ifdef HIL_MLC_DEBUG
237 	printk(KERN_DEBUG PREFIX "same in slot %i\n", rc);
238 #endif
239 	mlc->serio_map[rc].di_revmap = mlc->ddi;
240 	hil_mlc_clean_serio_map(mlc);
241 	return 0;
242  err:
243 	printk(KERN_ERR PREFIX "Residual device slots exhausted, close some serios!\n");
244 	return 1;
245 }
246 
247 /* An LCV used to prevent runaway loops, forces 5 second sleep when reset. */
hilse_init_lcv(hil_mlc * mlc,int unused)248 static int hilse_init_lcv(hil_mlc *mlc, int unused) {
249 	struct timeval tv;
250 
251 	do_gettimeofday(&tv);
252 
253 	if(mlc->lcv == 0) goto restart;  /* First init, no need to dally */
254 	if(tv.tv_sec - mlc->lcv_tv.tv_sec < 5) return -1;
255  restart:
256 	mlc->lcv_tv = tv;
257 	mlc->lcv = 0;
258 	return 0;
259 }
260 
hilse_inc_lcv(hil_mlc * mlc,int lim)261 static int hilse_inc_lcv(hil_mlc *mlc, int lim) {
262 	if (mlc->lcv++ >= lim) return -1;
263 	return 0;
264 }
265 
266 #if 0
267 static int hilse_set_lcv(hil_mlc *mlc, int val) {
268 	mlc->lcv = val;
269 	return 0;
270 }
271 #endif
272 
273 /* Management of the discovered device index (zero based, -1 means no devs) */
hilse_set_ddi(hil_mlc * mlc,int val)274 static int hilse_set_ddi(hil_mlc *mlc, int val) {
275 	mlc->ddi = val;
276 	hil_mlc_clear_di_map(mlc, val + 1);
277 	return 0;
278 }
279 
hilse_dec_ddi(hil_mlc * mlc,int unused)280 static int hilse_dec_ddi(hil_mlc *mlc, int unused) {
281 	mlc->ddi--;
282 	if (mlc->ddi <= -1) {
283 		mlc->ddi = -1;
284 		hil_mlc_clear_di_map(mlc, 0);
285 		return -1;
286 	}
287 	hil_mlc_clear_di_map(mlc, mlc->ddi + 1);
288 	return 0;
289 }
290 
hilse_inc_ddi(hil_mlc * mlc,int unused)291 static int hilse_inc_ddi(hil_mlc *mlc, int unused) {
292 	if (mlc->ddi >= 6) {
293 		BUG();
294 		return -1;
295 	}
296 	mlc->ddi++;
297 	return 0;
298 }
299 
hilse_take_idd(hil_mlc * mlc,int unused)300 static int hilse_take_idd(hil_mlc *mlc, int unused) {
301 	int i;
302 
303 	/* Help the state engine:
304 	 * Is this a real IDD response or just an echo?
305 	 *
306 	 * Real IDD response does not start with a command.
307 	 */
308 	if (mlc->ipacket[0] & HIL_PKT_CMD) goto bail;
309 	/* Should have the command echoed further down. */
310 	for (i = 1; i < 16; i++) {
311 		if (((mlc->ipacket[i] & HIL_PKT_ADDR_MASK) ==
312 		     (mlc->ipacket[0] & HIL_PKT_ADDR_MASK)) &&
313 		    (mlc->ipacket[i] & HIL_PKT_CMD) &&
314 		    ((mlc->ipacket[i] & HIL_PKT_DATA_MASK) == HIL_CMD_IDD))
315 			break;
316 	}
317 	if (i > 15) goto bail;
318 	/* And the rest of the packets should still be clear. */
319 	while (++i < 16) {
320 		if (mlc->ipacket[i]) break;
321 	}
322 	if (i < 16) goto bail;
323 	for (i = 0; i < 16; i++) {
324 		mlc->di_scratch.idd[i] =
325 			mlc->ipacket[i] & HIL_PKT_DATA_MASK;
326 	}
327 	/* Next step is to see if RSC supported */
328 	if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_RSC)
329 		return HILSEN_NEXT;
330 	if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD)
331 		return HILSEN_DOWN | 4;
332 	return 0;
333  bail:
334 	mlc->ddi--;
335 	return -1; /* This should send us off to ACF */
336 }
337 
hilse_take_rsc(hil_mlc * mlc,int unused)338 static int hilse_take_rsc(hil_mlc *mlc, int unused) {
339 	int i;
340 
341 	for (i = 0; i < 16; i++) {
342 		mlc->di_scratch.rsc[i] =
343 			mlc->ipacket[i] & HIL_PKT_DATA_MASK;
344 	}
345 	/* Next step is to see if EXD supported (IDD has already been read) */
346 	if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD)
347 		return HILSEN_NEXT;
348 	return 0;
349 }
350 
hilse_take_exd(hil_mlc * mlc,int unused)351 static int hilse_take_exd(hil_mlc *mlc, int unused) {
352 	int i;
353 
354 	for (i = 0; i < 16; i++) {
355 		mlc->di_scratch.exd[i] =
356 			mlc->ipacket[i] & HIL_PKT_DATA_MASK;
357 	}
358 	/* Next step is to see if RNM supported. */
359 	if (mlc->di_scratch.exd[0] & HIL_EXD_HEADER_RNM)
360 		return HILSEN_NEXT;
361 	return 0;
362 }
363 
hilse_take_rnm(hil_mlc * mlc,int unused)364 static int hilse_take_rnm(hil_mlc *mlc, int unused) {
365 	int i;
366 
367 	for (i = 0; i < 16; i++) {
368 		mlc->di_scratch.rnm[i] =
369 			mlc->ipacket[i] & HIL_PKT_DATA_MASK;
370 	}
371 	do {
372 	  char nam[17];
373 	  snprintf(nam, 16, "%s", mlc->di_scratch.rnm);
374 	  nam[16] = '\0';
375 	  printk(KERN_INFO PREFIX "Device name gotten: %s\n", nam);
376 	} while (0);
377 	return 0;
378 }
379 
hilse_operate(hil_mlc * mlc,int repoll)380 static int hilse_operate(hil_mlc *mlc, int repoll) {
381 
382 	if (mlc->opercnt == 0) hil_mlcs_probe = 0;
383 	mlc->opercnt = 1;
384 
385 	hil_mlc_send_polls(mlc);
386 
387 	if (!hil_mlcs_probe) return 0;
388 	hil_mlcs_probe = 0;
389 	mlc->opercnt = 0;
390 	return 1;
391 }
392 
393 #define FUNC(funct, funct_arg, zero_rc, neg_rc, pos_rc) \
394 { HILSE_FUNC,		{ func: &funct }, funct_arg, zero_rc, neg_rc, pos_rc },
395 #define OUT(pack) \
396 { HILSE_OUT,		{ packet: pack }, 0, HILSEN_NEXT, HILSEN_DOZE, 0 },
397 #define CTS \
398 { HILSE_CTS,		{ packet: 0    }, 0, HILSEN_NEXT | HILSEN_SCHED | HILSEN_BREAK, HILSEN_DOZE, 0 },
399 #define EXPECT(comp, to, got, got_wrong, timed_out) \
400 { HILSE_EXPECT,		{ packet: comp }, to, got, got_wrong, timed_out },
401 #define EXPECT_LAST(comp, to, got, got_wrong, timed_out) \
402 { HILSE_EXPECT_LAST,	{ packet: comp }, to, got, got_wrong, timed_out },
403 #define EXPECT_DISC(comp, to, got, got_wrong, timed_out) \
404 { HILSE_EXPECT_DISC,	{ packet: comp }, to, got, got_wrong, timed_out },
405 #define IN(to, got, got_error, timed_out) \
406 { HILSE_IN,		{ packet: 0    }, to, got, got_error, timed_out },
407 #define OUT_DISC(pack) \
408 { HILSE_OUT_DISC,	{ packet: pack }, 0, 0, 0, 0 },
409 #define OUT_LAST(pack) \
410 { HILSE_OUT_LAST,	{ packet: pack }, 0, 0, 0, 0 },
411 
412 struct hilse_node hil_mlc_se[HILSEN_END] = {
413 
414 	/* 0  HILSEN_START */
415 	FUNC(hilse_init_lcv, 0,	HILSEN_NEXT,	HILSEN_SLEEP,	0)
416 
417 	/* 1  HILSEN_RESTART */
418 	FUNC(hilse_inc_lcv, 10,	HILSEN_NEXT,	HILSEN_START,  0)
419 	OUT(HIL_CTRL_ONLY)			/* Disable APE */
420 	CTS
421 
422 #define TEST_PACKET(x) \
423 (HIL_PKT_CMD | (x << HIL_PKT_ADDR_SHIFT) | x << 4 | x)
424 
425 	OUT(HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET(0x5))
426 	EXPECT(HIL_ERR_INT | TEST_PACKET(0x5),
427 	       2000,		HILSEN_NEXT,	HILSEN_RESTART,	HILSEN_RESTART)
428 	OUT(HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET(0xa))
429 	EXPECT(HIL_ERR_INT | TEST_PACKET(0xa),
430 	       2000,		HILSEN_NEXT,	HILSEN_RESTART,	HILSEN_RESTART)
431 	OUT(HIL_CTRL_ONLY | 0)			/* Disable test mode */
432 
433 	/* 9  HILSEN_DHR */
434 	FUNC(hilse_init_lcv, 0,	HILSEN_NEXT,	HILSEN_SLEEP,	0)
435 
436 	/* 10 HILSEN_DHR2 */
437 	FUNC(hilse_inc_lcv, 10,	HILSEN_NEXT,	HILSEN_START,	0)
438 	FUNC(hilse_set_ddi, -1,	HILSEN_NEXT,	0,		0)
439 	OUT(HIL_PKT_CMD | HIL_CMD_DHR)
440 	IN(300000,		HILSEN_DHR2,	HILSEN_DHR2,	HILSEN_NEXT)
441 
442 	/* 14 HILSEN_IFC */
443   	OUT(HIL_PKT_CMD | HIL_CMD_IFC)
444 	EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT,
445 	       20000,		HILSEN_DISC,	HILSEN_DHR2,	HILSEN_NEXT )
446 
447 	/* If devices are there, they weren't in PUP or other loopback mode.
448 	 * We're more concerned at this point with restoring operation
449 	 * to devices than discovering new ones, so we try to salvage
450 	 * the loop configuration by closing off the loop.
451 	 */
452 
453 	/* 16 HILSEN_HEAL0 */
454 	FUNC(hilse_dec_ddi, 0,	HILSEN_NEXT,	HILSEN_ACF,	0)
455 	FUNC(hilse_inc_ddi, 0,	HILSEN_NEXT,	0,		0)
456 
457 	/* 18 HILSEN_HEAL */
458 	OUT_LAST(HIL_CMD_ELB)
459 	EXPECT_LAST(HIL_CMD_ELB | HIL_ERR_INT,
460 		    20000,	HILSEN_REPOLL,	HILSEN_DSR,	HILSEN_NEXT)
461 	FUNC(hilse_dec_ddi, 0,	HILSEN_HEAL,	HILSEN_NEXT,	0)
462 
463 	/* 21 HILSEN_ACF */
464 	FUNC(hilse_init_lcv, 0,	HILSEN_NEXT,	HILSEN_DOZE,	0)
465 
466 	/* 22 HILSEN_ACF2 */
467 	FUNC(hilse_inc_lcv, 10,	HILSEN_NEXT,	HILSEN_START,	0)
468 	OUT(HIL_PKT_CMD | HIL_CMD_ACF | 1)
469 	IN(20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_NEXT)
470 
471 	/* 25 HILSEN_DISC0 */
472 	OUT_DISC(HIL_PKT_CMD | HIL_CMD_ELB)
473 	EXPECT_DISC(HIL_PKT_CMD | HIL_CMD_ELB | HIL_ERR_INT,
474 	       20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_DSR)
475 
476 	/* Only enter here if response just received */
477 	/* 27 HILSEN_DISC */
478 	OUT_DISC(HIL_PKT_CMD | HIL_CMD_IDD)
479 	EXPECT_DISC(HIL_PKT_CMD | HIL_CMD_IDD | HIL_ERR_INT,
480 	       20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_START)
481 	FUNC(hilse_inc_ddi,  0,	HILSEN_NEXT,	HILSEN_START,	0)
482 	FUNC(hilse_take_idd, 0,	HILSEN_MATCH,	HILSEN_IFCACF,	HILSEN_FOLLOW)
483 	OUT_LAST(HIL_PKT_CMD | HIL_CMD_RSC)
484 	EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_RSC | HIL_ERR_INT,
485 	       30000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_DSR)
486 	FUNC(hilse_take_rsc, 0,	HILSEN_MATCH,	0,		HILSEN_FOLLOW)
487 	OUT_LAST(HIL_PKT_CMD | HIL_CMD_EXD)
488 	EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_EXD | HIL_ERR_INT,
489 	       30000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_DSR)
490 	FUNC(hilse_take_exd, 0,	HILSEN_MATCH,	0,		HILSEN_FOLLOW)
491 	OUT_LAST(HIL_PKT_CMD | HIL_CMD_RNM)
492 	EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_RNM | HIL_ERR_INT,
493 	       30000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_DSR)
494 	FUNC(hilse_take_rnm, 0, HILSEN_MATCH,	0,		0)
495 
496 	/* 40 HILSEN_MATCH */
497 	FUNC(hilse_match, 0,	HILSEN_NEXT,	HILSEN_NEXT,	/* TODO */ 0)
498 
499 	/* 41 HILSEN_OPERATE */
500 	OUT(HIL_PKT_CMD | HIL_CMD_POL)
501 	EXPECT(HIL_PKT_CMD | HIL_CMD_POL | HIL_ERR_INT,
502 	       20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_NEXT)
503 	FUNC(hilse_operate, 0,	HILSEN_OPERATE,	HILSEN_IFC,	HILSEN_NEXT)
504 
505 	/* 44 HILSEN_PROBE */
506 	OUT_LAST(HIL_PKT_CMD | HIL_CMD_EPT)
507 	IN(10000, 		HILSEN_DISC,	HILSEN_DSR,	HILSEN_NEXT)
508 	OUT_DISC(HIL_PKT_CMD | HIL_CMD_ELB)
509 	IN(10000,		HILSEN_DISC,	HILSEN_DSR,	HILSEN_NEXT)
510 	OUT(HIL_PKT_CMD | HIL_CMD_ACF | 1)
511 	IN(10000,		HILSEN_DISC0,	HILSEN_DSR,	HILSEN_NEXT)
512 	OUT_LAST(HIL_PKT_CMD | HIL_CMD_ELB)
513 	IN(10000,		HILSEN_OPERATE,	HILSEN_DSR,	HILSEN_DSR)
514 
515 	/* 52 HILSEN_DSR */
516 	FUNC(hilse_set_ddi, -1,	HILSEN_NEXT,	0,		0)
517 	OUT(HIL_PKT_CMD | HIL_CMD_DSR)
518 	IN(20000, 		HILSEN_DHR,	HILSEN_DHR,	HILSEN_IFC)
519 
520 	/* 55 HILSEN_REPOLL */
521 	OUT(HIL_PKT_CMD | HIL_CMD_RPL)
522 	EXPECT(HIL_PKT_CMD | HIL_CMD_RPL | HIL_ERR_INT,
523 	       20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_NEXT)
524 	FUNC(hilse_operate, 1,	HILSEN_OPERATE,	HILSEN_IFC,	HILSEN_PROBE)
525 
526 	/* 58 HILSEN_IFCACF */
527   	OUT(HIL_PKT_CMD | HIL_CMD_IFC)
528 	EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT,
529 	       20000,		HILSEN_ACF2,	HILSEN_DHR2,	HILSEN_HEAL)
530 
531 	/* 60 HILSEN_END */
532 };
533 
hilse_setup_input(hil_mlc * mlc,struct hilse_node * node)534 static inline void hilse_setup_input(hil_mlc *mlc, struct hilse_node *node) {
535 
536 	switch (node->act) {
537 	case HILSE_EXPECT_DISC:
538 		mlc->imatch = node->object.packet;
539 		mlc->imatch |= ((mlc->ddi + 2) << HIL_PKT_ADDR_SHIFT);
540 		break;
541 	case HILSE_EXPECT_LAST:
542 		mlc->imatch = node->object.packet;
543 		mlc->imatch |= ((mlc->ddi + 1) << HIL_PKT_ADDR_SHIFT);
544 		break;
545 	case HILSE_EXPECT:
546 		mlc->imatch = node->object.packet;
547 		break;
548 	case HILSE_IN:
549 		mlc->imatch = 0;
550 		break;
551 	default:
552 		BUG();
553 	}
554 	mlc->istarted = 1;
555 	mlc->intimeout = node->arg;
556 	do_gettimeofday(&(mlc->instart));
557 	mlc->icount = 15;
558 	memset(mlc->ipacket, 0, 16 * sizeof(hil_packet));
559 	if (down_trylock(&(mlc->isem))) BUG();
560 
561 	return;
562 }
563 
564 #ifdef HIL_MLC_DEBUG
565 static int doze = 0;
566 static int seidx; /* For debug */
567 static int kick = 1;
568 #endif
569 
hilse_donode(hil_mlc * mlc)570 static int hilse_donode (hil_mlc *mlc) {
571 	struct hilse_node *node;
572 	int nextidx = 0;
573 	int sched_long = 0;
574 	unsigned long flags;
575 
576 #ifdef HIL_MLC_DEBUG
577 	if (mlc->seidx && (mlc->seidx != seidx)  && mlc->seidx != 41 && mlc->seidx != 42 && mlc->seidx != 43) {
578 	  printk(KERN_DEBUG PREFIX "z%i \n%s {%i}", doze, kick ? "K" : "", mlc->seidx);
579 		doze = 0;
580 	}
581 	kick = 0;
582 
583 	seidx = mlc->seidx;
584 #endif
585 	node = hil_mlc_se + mlc->seidx;
586 
587 	switch (node->act) {
588 		int rc;
589 		hil_packet pack;
590 
591 	case HILSE_FUNC:
592 		if (node->object.func == NULL) break;
593 		rc = node->object.func(mlc, node->arg);
594 		nextidx = (rc > 0) ? node->ugly :
595 			((rc < 0) ? node->bad : node->good);
596 		if (nextidx == HILSEN_FOLLOW) nextidx = rc;
597 		break;
598 	case HILSE_EXPECT_LAST:
599 	case HILSE_EXPECT_DISC:
600 	case HILSE_EXPECT:
601 	case HILSE_IN:
602 		/* Already set up from previous HILSE_OUT_* */
603 		write_lock_irqsave(&(mlc->lock), flags);
604 		rc = mlc->in(mlc, node->arg);
605 		if (rc == 2)  {
606 			nextidx = HILSEN_DOZE;
607 			sched_long = 1;
608 			write_unlock_irqrestore(&(mlc->lock), flags);
609 			break;
610 		}
611 		if (rc == 1)		nextidx = node->ugly;
612 		else if (rc == 0)	nextidx = node->good;
613 		else			nextidx = node->bad;
614 		mlc->istarted = 0;
615 		write_unlock_irqrestore(&(mlc->lock), flags);
616 		break;
617 	case HILSE_OUT_LAST:
618 		write_lock_irqsave(&(mlc->lock), flags);
619 		pack = node->object.packet;
620 		pack |= ((mlc->ddi + 1) << HIL_PKT_ADDR_SHIFT);
621 		goto out;
622 	case HILSE_OUT_DISC:
623 		write_lock_irqsave(&(mlc->lock), flags);
624 		pack = node->object.packet;
625 		pack |= ((mlc->ddi + 2) << HIL_PKT_ADDR_SHIFT);
626 		goto out;
627 	case HILSE_OUT:
628 		write_lock_irqsave(&(mlc->lock), flags);
629 		pack = node->object.packet;
630 	out:
631 		if (mlc->istarted) goto out2;
632 		/* Prepare to receive input */
633 		if ((node + 1)->act & HILSE_IN)
634 			hilse_setup_input(mlc, node + 1);
635 
636 	out2:
637 		write_unlock_irqrestore(&(mlc->lock), flags);
638 
639 		if (down_trylock(&mlc->osem)) {
640 			nextidx = HILSEN_DOZE;
641 			break;
642 		}
643 		up(&mlc->osem);
644 
645 		write_lock_irqsave(&(mlc->lock), flags);
646 		if (!(mlc->ostarted)) {
647 			mlc->ostarted = 1;
648 			mlc->opacket = pack;
649 			mlc->out(mlc);
650 			nextidx = HILSEN_DOZE;
651 			write_unlock_irqrestore(&(mlc->lock), flags);
652 			break;
653 		}
654 		mlc->ostarted = 0;
655 		do_gettimeofday(&(mlc->instart));
656 		write_unlock_irqrestore(&(mlc->lock), flags);
657 		nextidx = HILSEN_NEXT;
658 		break;
659 	case HILSE_CTS:
660 		nextidx = mlc->cts(mlc) ? node->bad : node->good;
661 		break;
662 	default:
663 		BUG();
664 		nextidx = 0;
665 		break;
666 	}
667 
668 #ifdef HIL_MLC_DEBUG
669 	if (nextidx == HILSEN_DOZE) doze++;
670 #endif
671 
672 	while (nextidx & HILSEN_SCHED) {
673 		struct timeval tv;
674 
675 		if (!sched_long) goto sched;
676 
677 		do_gettimeofday(&tv);
678 		tv.tv_usec += 1000000 * (tv.tv_sec - mlc->instart.tv_sec);
679 		tv.tv_usec -= mlc->instart.tv_usec;
680 		if (tv.tv_usec >= mlc->intimeout) goto sched;
681 		tv.tv_usec = (mlc->intimeout - tv.tv_usec) * HZ / 1000000;
682 		if (!tv.tv_usec) goto sched;
683 		mod_timer(&hil_mlcs_kicker, jiffies + tv.tv_usec);
684 		break;
685 	sched:
686 		tasklet_schedule(&hil_mlcs_tasklet);
687 		break;
688 	}
689 	if (nextidx & HILSEN_DOWN) mlc->seidx += nextidx & HILSEN_MASK;
690 	else if (nextidx & HILSEN_UP) mlc->seidx -= nextidx & HILSEN_MASK;
691 	else mlc->seidx = nextidx & HILSEN_MASK;
692 
693 	if (nextidx & HILSEN_BREAK)	return 1;
694 	return 0;
695 }
696 
697 /******************** tasklet context functions **************************/
hil_mlcs_process(unsigned long unused)698 static void hil_mlcs_process(unsigned long unused) {
699 	struct list_head *tmp;
700 
701 	read_lock(&hil_mlcs_lock);
702 	list_for_each(tmp, &hil_mlcs) {
703 		struct hil_mlc *mlc = list_entry(tmp, hil_mlc, list);
704 		while (hilse_donode(mlc) == 0) {
705 #ifdef HIL_MLC_DEBUG
706 		  if (mlc->seidx != 41 &&
707 		      mlc->seidx != 42 &&
708 		      mlc->seidx != 43)
709 		    printk(KERN_DEBUG PREFIX " + ");
710 #endif
711 		};
712 	}
713 	read_unlock(&hil_mlcs_lock);
714 }
715 
716 /************************* Keepalive timer task *********************/
717 
hil_mlcs_timer(unsigned long data)718 void hil_mlcs_timer (unsigned long data) {
719 	hil_mlcs_probe = 1;
720 	tasklet_schedule(&hil_mlcs_tasklet);
721 	/* Re-insert the periodic task. */
722 	if (!timer_pending(&hil_mlcs_kicker))
723 		mod_timer(&hil_mlcs_kicker, jiffies + HZ);
724 }
725 
726 /******************** user/kernel context functions **********************/
727 
hil_mlc_serio_write(struct serio * serio,unsigned char c)728 static int hil_mlc_serio_write(struct serio *serio, unsigned char c) {
729 	struct hil_mlc_serio_map *map;
730 	struct hil_mlc *mlc;
731 	struct serio_dev *dev;
732 	uint8_t *idx, *last;
733 
734 	map = serio->driver;
735 	if (map == NULL) {
736 		BUG();
737 		return -EIO;
738 	}
739 	mlc = map->mlc;
740 	if (mlc == NULL) {
741 		BUG();
742 		return -EIO;
743 	}
744 	mlc->serio_opacket[map->didx] |=
745 		((hil_packet)c) << (8 * (3 - mlc->serio_oidx[map->didx]));
746 
747 	if (mlc->serio_oidx[map->didx] >= 3) {
748 		/* for now only commands */
749 		if (!(mlc->serio_opacket[map->didx] & HIL_PKT_CMD))
750 			return -EIO;
751 		switch (mlc->serio_opacket[map->didx] & HIL_PKT_DATA_MASK) {
752 		case HIL_CMD_IDD:
753 			idx = mlc->di[map->didx].idd;
754 			goto emu;
755 		case HIL_CMD_RSC:
756 			idx = mlc->di[map->didx].rsc;
757 			goto emu;
758 		case HIL_CMD_EXD:
759 			idx = mlc->di[map->didx].exd;
760 			goto emu;
761 		case HIL_CMD_RNM:
762 			idx = mlc->di[map->didx].rnm;
763 			goto emu;
764 		default:
765 			break;
766 		}
767 		mlc->serio_oidx[map->didx] = 0;
768 		mlc->serio_opacket[map->didx] = 0;
769 	}
770 
771 	mlc->serio_oidx[map->didx]++;
772 	return -EIO;
773  emu:
774 	dev = serio->dev;
775 	if (dev == NULL) {
776 		BUG();
777 		return -EIO;
778 	}
779 	last = idx + 15;
780 	while ((last != idx) && (*last == 0)) last--;
781 
782 	while (idx != last) {
783 		dev->interrupt(serio, 0, 0);
784 		dev->interrupt(serio, HIL_ERR_INT >> 16, 0);
785 		dev->interrupt(serio, 0, 0);
786 		dev->interrupt(serio, *idx, 0);
787 		idx++;
788 	}
789 	dev->interrupt(serio, 0, 0);
790 	dev->interrupt(serio, HIL_ERR_INT >> 16, 0);
791 	dev->interrupt(serio, HIL_PKT_CMD >> 8, 0);
792 	dev->interrupt(serio, *idx, 0);
793 
794 	mlc->serio_oidx[map->didx] = 0;
795 	mlc->serio_opacket[map->didx] = 0;
796 
797 	return 0;
798 }
799 
hil_mlc_serio_open(struct serio * serio)800 static int hil_mlc_serio_open(struct serio *serio) {
801 	struct hil_mlc_serio_map *map;
802 	struct hil_mlc *mlc;
803 
804 	if (serio->private != NULL) return -EBUSY;
805 
806 	map = serio->driver;
807 	if (map == NULL) {
808 		BUG();
809 		return -ENODEV;
810 	}
811 	mlc = map->mlc;
812 	if (mlc == NULL) {
813 		BUG();
814 		return -ENODEV;
815 	}
816 
817 	mlc->inc_use_count();
818 
819 	return 0;
820 }
821 
hil_mlc_serio_close(struct serio * serio)822 static void hil_mlc_serio_close(struct serio *serio) {
823 	struct hil_mlc_serio_map *map;
824 	struct hil_mlc *mlc;
825 
826 	map = serio->driver;
827 	if (map == NULL) {
828 		BUG();
829 		return;
830 	}
831 	mlc = map->mlc;
832 	if (mlc == NULL) {
833 		BUG();
834 		return;
835 	}
836 
837 	mlc->dec_use_count();
838 
839 	serio->private = NULL;
840 	serio->dev = NULL;
841 	/* TODO wake up interruptable */
842 }
843 
hil_mlc_register(hil_mlc * mlc)844 int hil_mlc_register(hil_mlc *mlc) {
845 	int i;
846         unsigned long flags;
847 
848 	MOD_INC_USE_COUNT;
849 	if (mlc == NULL) {
850 		MOD_DEC_USE_COUNT;
851 		return -EINVAL;
852 	}
853 
854 	mlc->istarted = 0;
855         mlc->ostarted = 0;
856 
857         mlc->lock = RW_LOCK_UNLOCKED;
858         init_MUTEX(&(mlc->osem));
859 
860         init_MUTEX(&(mlc->isem));
861         mlc->icount = -1;
862         mlc->imatch = 0;
863 
864 	mlc->opercnt = 0;
865 
866         init_MUTEX_LOCKED(&(mlc->csem));
867 
868 	hil_mlc_clear_di_scratch(mlc);
869 	hil_mlc_clear_di_map(mlc, 0);
870 	for (i = 0; i < HIL_MLC_DEVMEM; i++) {
871 		hil_mlc_copy_di_scratch(mlc, i);
872 		memset(&(mlc->serio[i]), 0, sizeof(mlc->serio[0]));
873 		mlc->serio[i].type		= SERIO_HIL | SERIO_HIL_MLC;
874 		mlc->serio[i].write		= hil_mlc_serio_write;
875 		mlc->serio[i].open		= hil_mlc_serio_open;
876 		mlc->serio[i].close		= hil_mlc_serio_close;
877 		mlc->serio[i].driver		= &(mlc->serio_map[i]);
878 		mlc->serio_map[i].mlc		= mlc;
879 		mlc->serio_map[i].didx		= i;
880 		mlc->serio_map[i].di_revmap	= -1;
881 		mlc->serio_opacket[i]		= 0;
882 		mlc->serio_oidx[i]		= 0;
883 		serio_register_port(&(mlc->serio[i]));
884 	}
885 
886 	mlc->tasklet = &hil_mlcs_tasklet;
887 
888 	write_lock_irqsave(&hil_mlcs_lock, flags);
889 	list_add_tail(&mlc->list, &hil_mlcs);
890 	mlc->seidx = HILSEN_START;
891 	write_unlock_irqrestore(&hil_mlcs_lock, flags);
892 
893 	tasklet_schedule(&hil_mlcs_tasklet);
894 	return 0;
895 }
896 
hil_mlc_unregister(hil_mlc * mlc)897 int hil_mlc_unregister(hil_mlc *mlc) {
898 	struct list_head *tmp;
899         unsigned long flags;
900 	int i;
901 
902 	if (mlc == NULL)
903 		return -EINVAL;
904 
905 	write_lock_irqsave(&hil_mlcs_lock, flags);
906 	list_for_each(tmp, &hil_mlcs) {
907 		if (list_entry(tmp, hil_mlc, list) == mlc)
908 			goto found;
909 	}
910 
911 	/* not found in list */
912 	write_unlock_irqrestore(&hil_mlcs_lock, flags);
913 	tasklet_schedule(&hil_mlcs_tasklet);
914 	return -ENODEV;
915 
916  found:
917 	list_del(tmp);
918         write_unlock_irqrestore(&hil_mlcs_lock, flags);
919 	MOD_DEC_USE_COUNT;
920 
921 	for (i = 0; i < HIL_MLC_DEVMEM; i++)
922 		serio_unregister_port(&(mlc->serio[i]));
923 
924 	tasklet_schedule(&hil_mlcs_tasklet);
925 	return 0;
926 }
927 
928 /**************************** Module interface *************************/
929 
hil_mlc_init(void)930 static int __init hil_mlc_init(void)
931 {
932 	init_timer(&hil_mlcs_kicker);
933 	hil_mlcs_kicker.expires = jiffies + HZ;
934 	hil_mlcs_kicker.function = &hil_mlcs_timer;
935 	add_timer(&hil_mlcs_kicker);
936 
937 	tasklet_enable(&hil_mlcs_tasklet);
938 
939 	return 0;
940 }
941 
hil_mlc_exit(void)942 static void __exit hil_mlc_exit(void)
943 {
944 	del_timer(&hil_mlcs_kicker);
945 
946 	tasklet_disable(&hil_mlcs_tasklet);
947 	tasklet_kill(&hil_mlcs_tasklet);
948 }
949 
950 module_init(hil_mlc_init);
951 module_exit(hil_mlc_exit);
952