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