Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/drv/char/i8042/i8042.c

    r876f6463 rcccdb8b7  
    2929 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    3030 */
    31 
    3231/** @addtogroup kbd_port
    3332 * @ingroup kbd
    3433 * @{
    3534 */
    36 
    3735/** @file
    3836 * @brief i8042 PS/2 port driver.
     
    4644#include <str_error.h>
    4745#include <inttypes.h>
     46
    4847#include <ddf/log.h>
    4948#include <ddf/interrupt.h>
     49
    5050#include "i8042.h"
    5151
    52 /* Interesting bits for status register */
    53 #define i8042_OUTPUT_FULL  0x01
    54 #define i8042_INPUT_FULL   0x02
    55 #define i8042_AUX_DATA     0x20
    56 
    57 /* Command constants */
    58 #define i8042_CMD_WRITE_CMDB  0x60  /**< Write command byte */
    59 #define i8042_CMD_WRITE_AUX   0xd4  /**< Write aux device */
    60 
    61 /* Command byte fields */
    62 #define i8042_KBD_IE         0x01
    63 #define i8042_AUX_IE         0x02
    64 #define i8042_KBD_DISABLE    0x10
    65 #define i8042_AUX_DISABLE    0x20
    66 #define i8042_KBD_TRANSLATE  0x40  /* Use this to switch to XT scancodes */
    67 
    68 #define CHECK_RET_DESTROY(ret, msg...) \
    69         do { \
    70                 if (ret != EOK) { \
    71                         ddf_msg(LVL_ERROR, msg); \
    72                         if (dev->kbd_fun) { \
    73                                 dev->kbd_fun->driver_data = NULL; \
    74                                 ddf_fun_destroy(dev->kbd_fun); \
    75                         } \
    76                         if (dev->aux_fun) { \
    77                                 dev->aux_fun->driver_data = NULL; \
    78                                 ddf_fun_destroy(dev->aux_fun); \
    79                         } \
    80                 } \
    81         } while (0)
    82 
    83 #define CHECK_RET_UNBIND_DESTROY(ret, msg...) \
    84         do { \
    85                 if (ret != EOK) { \
    86                         ddf_msg(LVL_ERROR, msg); \
    87                         if (dev->kbd_fun) { \
    88                                 ddf_fun_unbind(dev->kbd_fun); \
    89                                 dev->kbd_fun->driver_data = NULL; \
    90                                 ddf_fun_destroy(dev->kbd_fun); \
    91                         } \
    92                         if (dev->aux_fun) { \
    93                                 ddf_fun_unbind(dev->aux_fun); \
    94                                 dev->aux_fun->driver_data = NULL; \
    95                                 ddf_fun_destroy(dev->aux_fun); \
    96                         } \
    97                 } \
    98         } while (0)
     52#define NAME       "i8042"
    9953
    10054void default_handler(ddf_fun_t *, ipc_callid_t, ipc_call_t *);
     
    10559};
    10660
     61/* Interesting bits for status register */
     62#define i8042_OUTPUT_FULL       0x01
     63#define i8042_INPUT_FULL        0x02
     64#define i8042_AUX_DATA          0x20
     65
     66/* Command constants */
     67#define i8042_CMD_WRITE_CMDB    0x60    /**< write command byte */
     68#define i8042_CMD_WRITE_AUX     0xd4    /**< write aux device */
     69
     70/* Command byte fields */
     71#define i8042_KBD_IE            0x01
     72#define i8042_AUX_IE            0x02
     73#define i8042_KBD_DISABLE       0x10
     74#define i8042_AUX_DISABLE       0x20
     75#define i8042_KBD_TRANSLATE     0x40 /* Use this to switch to XT scancodes */
     76
     77static const irq_pio_range_t i8042_ranges[] = {
     78        {
     79                .base = 0,
     80                .size = sizeof(i8042_regs_t)
     81        }
     82};
     83
    10784/** i8042 Interrupt pseudo-code. */
    10885static const irq_cmd_t i8042_cmds[] = {
    10986        {
    11087                .cmd = CMD_PIO_READ_8,
    111                 .addr = NULL,  /* will be patched in run-time */
     88                .addr = NULL,   /* will be patched in run-time */
    11289                .dstarg = 1
    11390        },
     
    125102        {
    126103                .cmd = CMD_PIO_READ_8,
    127                 .addr = NULL,  /* will be patched in run-time */
     104                .addr = NULL,   /* will be patched in run-time */
    128105                .dstarg = 2
    129106        },
     
    141118
    142119/** Interrupt handler routine.
    143  *
    144  * Write new data to the corresponding buffer.
    145  *
    146  * @param dev  Device that caued the interrupt.
    147  * @param iid  Call id.
     120 * Writes new data to the corresponding buffer.
     121 * @param dev Device that caued the interrupt.
     122 * @param iid Call id.
    148123 * @param call pointerr to call data.
    149  *
    150  */
    151 static void i8042_irq_handler(ddf_dev_t *dev, ipc_callid_t iid,
    152     ipc_call_t *call)
    153 {
    154         if ((!dev) || (!dev->driver_data))
     124 */
     125static void i8042_irq_handler(
     126    ddf_dev_t *dev, ipc_callid_t iid, ipc_call_t *call)
     127{
     128        if (!dev || !dev->driver_data)
    155129                return;
    156        
    157130        i8042_t *controller = dev->driver_data;
    158        
     131
    159132        const uint8_t status = IPC_GET_ARG1(*call);
    160133        const uint8_t data = IPC_GET_ARG2(*call);
    161        
    162134        buffer_t *buffer = (status & i8042_AUX_DATA) ?
    163135            &controller->aux_buffer : &controller->kbd_buffer;
    164        
    165136        buffer_write(buffer, data);
    166137}
    167138
    168139/** Initialize i8042 driver structure.
    169  *
    170  * @param dev       Driver structure to initialize.
    171  * @param regs      I/O address of registers.
    172  * @param reg_size  size of the reserved I/O address space.
    173  * @param irq_kbd   IRQ for primary port.
     140 * @param dev Driver structure to initialize.
     141 * @param regs I/O address of registers.
     142 * @param reg_size size of the reserved I/O address space.
     143 * @param irq_kbd IRQ for primary port.
    174144 * @param irq_mouse IRQ for aux port.
    175  * @param ddf_dev   DDF device structure of the device.
    176  *
     145 * @param ddf_dev DDF device structure of the device.
    177146 * @return Error code.
    178  *
    179147 */
    180148int i8042_init(i8042_t *dev, void *regs, size_t reg_size, int irq_kbd,
     
    183151        assert(ddf_dev);
    184152        assert(dev);
    185        
     153
    186154        if (reg_size < sizeof(i8042_regs_t))
    187155                return EINVAL;
    188        
    189         if (pio_enable(regs, sizeof(i8042_regs_t), (void **) &dev->regs) != 0)
     156
     157        if (pio_enable(regs, sizeof(i8042_regs_t), (void**)&dev->regs) != 0)
    190158                return -1;
    191        
     159
    192160        dev->kbd_fun = ddf_fun_create(ddf_dev, fun_inner, "ps2a");
    193161        if (!dev->kbd_fun)
    194162                return ENOMEM;
    195        
    196163        int ret = ddf_fun_add_match_id(dev->kbd_fun, "char/xtkbd", 90);
    197164        if (ret != EOK) {
     
    199166                return ret;
    200167        }
    201        
     168
    202169        dev->aux_fun = ddf_fun_create(ddf_dev, fun_inner, "ps2b");
    203170        if (!dev->aux_fun) {
     
    205172                return ENOMEM;
    206173        }
    207        
     174
    208175        ret = ddf_fun_add_match_id(dev->aux_fun, "char/ps2mouse", 90);
    209176        if (ret != EOK) {
     
    212179                return ret;
    213180        }
    214        
     181
    215182        dev->kbd_fun->ops = &ops;
    216183        dev->aux_fun->ops = &ops;
    217184        dev->kbd_fun->driver_data = dev;
    218185        dev->aux_fun->driver_data = dev;
    219        
     186
    220187        buffer_init(&dev->kbd_buffer, dev->kbd_data, BUFFER_SIZE);
    221188        buffer_init(&dev->aux_buffer, dev->aux_data, BUFFER_SIZE);
    222189        fibril_mutex_initialize(&dev->write_guard);
    223        
     190
     191#define CHECK_RET_DESTROY(ret, msg...) \
     192if  (ret != EOK) { \
     193        ddf_msg(LVL_ERROR, msg); \
     194        if (dev->kbd_fun) { \
     195                dev->kbd_fun->driver_data = NULL; \
     196                ddf_fun_destroy(dev->kbd_fun); \
     197        } \
     198        if (dev->aux_fun) { \
     199                dev->aux_fun->driver_data = NULL; \
     200                ddf_fun_destroy(dev->aux_fun); \
     201        } \
     202} else (void)0
     203
    224204        ret = ddf_fun_bind(dev->kbd_fun);
    225         CHECK_RET_DESTROY(ret, "Failed to bind keyboard function: %s.",
    226             str_error(ret));
    227        
     205        CHECK_RET_DESTROY(ret,
     206            "Failed to bind keyboard function: %s.", str_error(ret));
     207
    228208        ret = ddf_fun_bind(dev->aux_fun);
    229         CHECK_RET_DESTROY(ret, "Failed to bind mouse function: %s.",
    230             str_error(ret));
    231        
     209        CHECK_RET_DESTROY(ret,
     210            "Failed to bind mouse function: %s.", str_error(ret));
     211
    232212        /* Disable kbd and aux */
    233213        wait_ready(dev);
     
    235215        wait_ready(dev);
    236216        pio_write_8(&dev->regs->data, i8042_KBD_DISABLE | i8042_AUX_DISABLE);
    237        
     217
    238218        /* Flush all current IO */
    239219        while (pio_read_8(&dev->regs->status) & i8042_OUTPUT_FULL)
    240220                (void) pio_read_8(&dev->regs->data);
    241        
     221
     222#define CHECK_RET_UNBIND_DESTROY(ret, msg...) \
     223if  (ret != EOK) { \
     224        ddf_msg(LVL_ERROR, msg); \
     225        if (dev->kbd_fun) { \
     226                ddf_fun_unbind(dev->kbd_fun); \
     227                dev->kbd_fun->driver_data = NULL; \
     228                ddf_fun_destroy(dev->kbd_fun); \
     229        } \
     230        if (dev->aux_fun) { \
     231                ddf_fun_unbind(dev->aux_fun); \
     232                dev->aux_fun->driver_data = NULL; \
     233                ddf_fun_destroy(dev->aux_fun); \
     234        } \
     235} else (void)0
     236
     237        const size_t range_count = sizeof(i8042_ranges) /
     238            sizeof(irq_pio_range_t);
     239        irq_pio_range_t ranges[range_count];
     240        memcpy(ranges, i8042_ranges, sizeof(i8042_ranges));
     241        ranges[0].base = (uintptr_t) regs;
     242
    242243        const size_t cmd_count = sizeof(i8042_cmds) / sizeof(irq_cmd_t);
    243244        irq_cmd_t cmds[cmd_count];
    244245        memcpy(cmds, i8042_cmds, sizeof(i8042_cmds));
    245         cmds[0].addr = (void *) &dev->regs->status;
    246         cmds[3].addr = (void *) &dev->regs->data;
    247        
     246        cmds[0].addr = (void *) &(((i8042_regs_t *) regs)->status);
     247        cmds[3].addr = (void *) &(((i8042_regs_t *) regs)->data);
     248
    248249        irq_code_t irq_code = {
     250                .rangecount = range_count,
     251                .ranges = ranges,
    249252                .cmdcount = cmd_count,
    250253                .cmds = cmds
    251254        };
    252        
    253255        ret = register_interrupt_handler(ddf_dev, irq_kbd, i8042_irq_handler,
    254256            &irq_code);
    255         CHECK_RET_UNBIND_DESTROY(ret, "Failed set handler for kbd: %s.",
    256             str_error(ret));
    257        
     257        CHECK_RET_UNBIND_DESTROY(ret,
     258            "Failed set handler for kbd: %s.", str_error(ret));
     259
    258260        ret = register_interrupt_handler(ddf_dev, irq_mouse, i8042_irq_handler,
    259261            &irq_code);
    260         CHECK_RET_UNBIND_DESTROY(ret, "Failed set handler for mouse: %s.",
    261             str_error(ret));
    262        
     262        CHECK_RET_UNBIND_DESTROY(ret,
     263            "Failed set handler for mouse: %s.", str_error(ret));
     264
    263265        /* Enable interrupts */
    264266        async_sess_t *parent_sess =
     
    267269        ret = parent_sess ? EOK : ENOMEM;
    268270        CHECK_RET_UNBIND_DESTROY(ret, "Failed to create parent connection.");
    269        
     271
    270272        const bool enabled = hw_res_enable_interrupt(parent_sess);
    271273        async_hangup(parent_sess);
    272274        ret = enabled ? EOK : EIO;
    273275        CHECK_RET_UNBIND_DESTROY(ret, "Failed to enable interrupts: %s.");
    274        
     276
    275277        /* Enable port interrupts. */
    276278        wait_ready(dev);
     
    279281        pio_write_8(&dev->regs->data, i8042_KBD_IE | i8042_KBD_TRANSLATE |
    280282            i8042_AUX_IE);
    281        
     283
    282284        return EOK;
    283285}
    284286
    285 // FIXME TODO use shared instead this
     287// TODO use shared instead this
    286288enum {
    287289        IPC_CHAR_READ = DEV_FIRST_CUSTOM_METHOD,
     
    290292
    291293/** Write data to i8042 port.
    292  *
    293  * @param fun    DDF function.
     294 * @param fun DDF function.
    294295 * @param buffer Data source.
    295  * @param size   Data size.
    296  *
     296 * @param size Data size.
    297297 * @return Bytes written.
    298  *
    299298 */
    300299static int i8042_write(ddf_fun_t *fun, char *buffer, size_t size)
     
    302301        assert(fun);
    303302        assert(fun->driver_data);
    304        
    305303        i8042_t *controller = fun->driver_data;
    306304        fibril_mutex_lock(&controller->write_guard);
    307        
    308305        for (size_t i = 0; i < size; ++i) {
    309                 if (controller->aux_fun == fun) {
    310                         wait_ready(controller);
    311                         pio_write_8(&controller->regs->status,
    312                             i8042_CMD_WRITE_AUX);
    313                 }
    314                
    315306                wait_ready(controller);
     307                if (controller->aux_fun == fun)
     308                        pio_write_8(
     309                            &controller->regs->status, i8042_CMD_WRITE_AUX);
    316310                pio_write_8(&controller->regs->data, buffer[i]);
    317311        }
    318        
    319312        fibril_mutex_unlock(&controller->write_guard);
    320313        return size;
     
    322315
    323316/** Read data from i8042 port.
    324  *
    325  * @param fun    DDF function.
     317 * @param fun DDF function.
    326318 * @param buffer Data place.
    327  * @param size   Data place size.
    328  *
     319 * @param size Data place size.
    329320 * @return Bytes read.
    330  *
    331321 */
    332322static int i8042_read(ddf_fun_t *fun, char *data, size_t size)
     
    334324        assert(fun);
    335325        assert(fun->driver_data);
    336        
     326
    337327        i8042_t *controller = fun->driver_data;
    338328        buffer_t *buffer = (fun == controller->aux_fun) ?
    339329            &controller->aux_buffer : &controller->kbd_buffer;
    340        
    341         for (size_t i = 0; i < size; ++i)
     330        for (size_t i = 0; i < size; ++i) {
    342331                *data++ = buffer_read(buffer);
    343        
     332        }
    344333        return size;
    345334}
    346335
    347336/** Handle data requests.
    348  *
    349  * @param fun  ddf_fun_t function.
    350  * @param id   callid
     337 * @param fun ddf_fun_t function.
     338 * @param id callid
    351339 * @param call IPC request.
    352  *
    353340 */
    354341void default_handler(ddf_fun_t *fun, ipc_callid_t id, ipc_call_t *call)
     
    356343        const sysarg_t method = IPC_GET_IMETHOD(*call);
    357344        const size_t size = IPC_GET_ARG1(*call);
    358        
    359345        switch (method) {
    360346        case IPC_CHAR_READ:
    361347                if (size <= 4 * sizeof(sysarg_t)) {
    362348                        sysarg_t message[4] = {};
    363                        
    364                         i8042_read(fun, (char *) message, size);
     349                        i8042_read(fun, (char*)message, size);
    365350                        async_answer_4(id, size, message[0], message[1],
    366351                            message[2], message[3]);
    367                 } else
     352                } else {
    368353                        async_answer_0(id, ELIMIT);
     354                }
    369355                break;
    370        
     356
    371357        case IPC_CHAR_WRITE:
    372358                if (size <= 3 * sizeof(sysarg_t)) {
    373359                        const sysarg_t message[3] = {
    374                                 IPC_GET_ARG2(*call),
    375                                 IPC_GET_ARG3(*call),
    376                                 IPC_GET_ARG4(*call)
    377                         };
    378                        
    379                         i8042_write(fun, (char *) message, size);
     360                                IPC_GET_ARG2(*call), IPC_GET_ARG3(*call),
     361                                IPC_GET_ARG4(*call) };
     362                        i8042_write(fun, (char*)message, size);
    380363                        async_answer_0(id, size);
    381                 } else
     364                } else {
    382365                        async_answer_0(id, ELIMIT);
    383        
     366                }
     367
    384368        default:
    385369                async_answer_0(id, EINVAL);
    386370        }
    387371}
    388 
    389372/**
    390373 * @}
Note: See TracChangeset for help on using the changeset viewer.