linux-old/drivers/hil/hil_mlc.c
<<
>>
Prefs
   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
  65MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
  66MODULE_DESCRIPTION("HIL MLC serio");
  67MODULE_LICENSE("Dual BSD/GPL");
  68
  69EXPORT_SYMBOL(hil_mlc_register);
  70EXPORT_SYMBOL(hil_mlc_unregister);
  71
  72#define PREFIX "HIL MLC: "
  73
  74static LIST_HEAD(hil_mlcs);
  75static rwlock_t                 hil_mlcs_lock = RW_LOCK_UNLOCKED;
  76static struct timer_list        hil_mlcs_kicker;
  77static int                      hil_mlcs_probe;
  78
  79static void hil_mlcs_process(unsigned long unused);
  80DECLARE_TASKLET_DISABLED(hil_mlcs_tasklet, hil_mlcs_process, 0);
  81
  82
  83/* #define HIL_MLC_DEBUG */
  84
  85/********************** Device info/instance management **********************/
  86
  87static 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
  94static void hil_mlc_clear_di_scratch (hil_mlc *mlc) {
  95        memset(&(mlc->di_scratch), 0, sizeof(mlc->di_scratch));
  96}
  97
  98static 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
 102static 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
 121static 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
 137static 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
 149static 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
 219static 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. */
 248static 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
 261static int hilse_inc_lcv(hil_mlc *mlc, int lim) {
 262        if (mlc->lcv++ >= lim) return -1;
 263        return 0;
 264}
 265
 266#if 0
 267static 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) */
 274static 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
 280static 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
 291static 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
 300static 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
 338static 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
 351static 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
 364static 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
 380static 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
 412struct 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
 534static 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
 565static int doze = 0;
 566static int seidx; /* For debug */
 567static int kick = 1;
 568#endif
 569
 570static 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 **************************/
 698static 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
 718void 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
 728static 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
 800static 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
 822static 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
 844int 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
 897int 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
 930static 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                
 942static 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                        
 950module_init(hil_mlc_init);
 951module_exit(hil_mlc_exit);
 952
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.