Ignore:
File:
1 edited

Legend:

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

    rca4730a5 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.
    3937 */
    4038
     39#include <devman.h>
    4140#include <device/hw_res.h>
    4241#include <ddi.h>
     42#include <libarch/ddi.h>
    4343#include <errno.h>
    4444#include <str_error.h>
    4545#include <inttypes.h>
     46
    4647#include <ddf/log.h>
    4748#include <ddf/interrupt.h>
     49
    4850#include "i8042.h"
    4951
    50 /* Interesting bits for status register */
    51 #define i8042_OUTPUT_FULL  0x01
    52 #define i8042_INPUT_FULL   0x02
    53 #define i8042_AUX_DATA     0x20
    54 
    55 /* Command constants */
    56 #define i8042_CMD_WRITE_CMDB  0x60  /**< Write command byte */
    57 #define i8042_CMD_WRITE_AUX   0xd4  /**< Write aux device */
    58 
    59 /* Command byte fields */
    60 #define i8042_KBD_IE         0x01
    61 #define i8042_AUX_IE         0x02
    62 #define i8042_KBD_DISABLE    0x10
    63 #define i8042_AUX_DISABLE    0x20
    64 #define i8042_KBD_TRANSLATE  0x40  /* Use this to switch to XT scancodes */
     52#define NAME       "i8042"
    6553
    6654void default_handler(ddf_fun_t *, ipc_callid_t, ipc_call_t *);
     
    7159};
    7260
     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
    7377static const irq_pio_range_t i8042_ranges[] = {
    7478        {
     
    8286        {
    8387                .cmd = CMD_PIO_READ_8,
    84                 .addr = NULL,  /* will be patched in run-time */
     88                .addr = NULL,   /* will be patched in run-time */
    8589                .dstarg = 1
    8690        },
    8791        {
    88                 .cmd = CMD_AND,
     92                .cmd = CMD_BTEST,
    8993                .value = i8042_OUTPUT_FULL,
    9094                .srcarg = 1,
     
    98102        {
    99103                .cmd = CMD_PIO_READ_8,
    100                 .addr = NULL,  /* will be patched in run-time */
     104                .addr = NULL,   /* will be patched in run-time */
    101105                .dstarg = 2
    102106        },
     
    106110};
    107111
    108 /** Get i8042 soft state from device node. */
    109 static i8042_t *dev_i8042(ddf_dev_t *dev)
    110 {
    111         return ddf_dev_data_get(dev);
    112 }
    113 
    114112/** Wait until it is safe to write to the device. */
    115113static void wait_ready(i8042_t *dev)
     
    120118
    121119/** Interrupt handler routine.
    122  *
    123  * Write new data to the corresponding buffer.
    124  *
    125  * @param dev  Device that caued the interrupt.
    126  * @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.
    127123 * @param call pointerr to call data.
    128  *
    129  */
    130 static void i8042_irq_handler(ddf_dev_t *dev, ipc_callid_t iid,
    131     ipc_call_t *call)
    132 {
    133         i8042_t *controller = dev_i8042(dev);
    134        
     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)
     129                return;
     130        i8042_t *controller = dev->driver_data;
     131
    135132        const uint8_t status = IPC_GET_ARG1(*call);
    136133        const uint8_t data = IPC_GET_ARG2(*call);
    137        
    138134        buffer_t *buffer = (status & i8042_AUX_DATA) ?
    139135            &controller->aux_buffer : &controller->kbd_buffer;
    140        
    141136        buffer_write(buffer, data);
    142137}
    143138
    144139/** Initialize i8042 driver structure.
    145  *
    146  * @param dev       Driver structure to initialize.
    147  * @param regs      I/O address of registers.
    148  * @param reg_size  size of the reserved I/O address space.
    149  * @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.
    150144 * @param irq_mouse IRQ for aux port.
    151  * @param ddf_dev   DDF device structure of the device.
    152  *
     145 * @param ddf_dev DDF device structure of the device.
    153146 * @return Error code.
    154  *
    155147 */
    156148int i8042_init(i8042_t *dev, void *regs, size_t reg_size, int irq_kbd,
    157149    int irq_mouse, ddf_dev_t *ddf_dev)
    158150{
    159         const size_t range_count = sizeof(i8042_ranges) /
    160             sizeof(irq_pio_range_t);
    161         irq_pio_range_t ranges[range_count];
    162         const size_t cmd_count = sizeof(i8042_cmds) / sizeof(irq_cmd_t);
    163         irq_cmd_t cmds[cmd_count];
    164 
    165         int rc;
    166         bool kbd_bound = false;
    167         bool aux_bound = false;
    168 
    169         dev->kbd_fun = NULL;
    170         dev->aux_fun = NULL;
    171        
    172         if (reg_size < sizeof(i8042_regs_t)) {
    173                 rc = EINVAL;
    174                 goto error;
    175         }
    176        
    177         if (pio_enable(regs, sizeof(i8042_regs_t), (void **) &dev->regs) != 0) {
    178                 rc = EIO;
    179                 goto error;
    180         }
    181        
     151        assert(ddf_dev);
     152        assert(dev);
     153
     154        if (reg_size < sizeof(i8042_regs_t))
     155                return EINVAL;
     156
     157        if (pio_enable(regs, sizeof(i8042_regs_t), (void**)&dev->regs) != 0)
     158                return -1;
     159
    182160        dev->kbd_fun = ddf_fun_create(ddf_dev, fun_inner, "ps2a");
    183         if (dev->kbd_fun == NULL) {
    184                 rc = ENOMEM;
    185                 goto error;
    186         };
    187        
    188         rc = ddf_fun_add_match_id(dev->kbd_fun, "char/xtkbd", 90);
    189         if (rc != EOK)
    190                 goto error;
    191        
     161        if (!dev->kbd_fun)
     162                return ENOMEM;
     163        int ret = ddf_fun_add_match_id(dev->kbd_fun, "char/xtkbd", 90);
     164        if (ret != EOK) {
     165                ddf_fun_destroy(dev->kbd_fun);
     166                return ret;
     167        }
     168
    192169        dev->aux_fun = ddf_fun_create(ddf_dev, fun_inner, "ps2b");
    193         if (dev->aux_fun == NULL) {
    194                 rc = ENOMEM;
    195                 goto error;
    196         }
    197        
    198         rc = ddf_fun_add_match_id(dev->aux_fun, "char/ps2mouse", 90);
    199         if (rc != EOK)
    200                 goto error;
    201        
    202         ddf_fun_set_ops(dev->kbd_fun, &ops);
    203         ddf_fun_set_ops(dev->aux_fun, &ops);
    204        
     170        if (!dev->aux_fun) {
     171                ddf_fun_destroy(dev->kbd_fun);
     172                return ENOMEM;
     173        }
     174
     175        ret = ddf_fun_add_match_id(dev->aux_fun, "char/ps2mouse", 90);
     176        if (ret != EOK) {
     177                ddf_fun_destroy(dev->kbd_fun);
     178                ddf_fun_destroy(dev->aux_fun);
     179                return ret;
     180        }
     181
     182        dev->kbd_fun->ops = &ops;
     183        dev->aux_fun->ops = &ops;
     184        dev->kbd_fun->driver_data = dev;
     185        dev->aux_fun->driver_data = dev;
     186
    205187        buffer_init(&dev->kbd_buffer, dev->kbd_data, BUFFER_SIZE);
    206188        buffer_init(&dev->aux_buffer, dev->aux_data, BUFFER_SIZE);
    207189        fibril_mutex_initialize(&dev->write_guard);
    208        
    209         rc = ddf_fun_bind(dev->kbd_fun);
    210         if (rc != EOK) {
    211                 ddf_msg(LVL_ERROR, "Failed to bind keyboard function: %s.",
    212                     ddf_fun_get_name(dev->kbd_fun));
    213                 goto error;
    214         }
    215         kbd_bound = true;
    216        
    217         rc = ddf_fun_bind(dev->aux_fun);
    218         if (rc != EOK) {
    219                 ddf_msg(LVL_ERROR, "Failed to bind aux function: %s.",
    220                     ddf_fun_get_name(dev->aux_fun));
    221                 goto error;
    222         }
    223         aux_bound = true;
    224        
     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
     204        ret = ddf_fun_bind(dev->kbd_fun);
     205        CHECK_RET_DESTROY(ret,
     206            "Failed to bind keyboard function: %s.", str_error(ret));
     207
     208        ret = ddf_fun_bind(dev->aux_fun);
     209        CHECK_RET_DESTROY(ret,
     210            "Failed to bind mouse function: %s.", str_error(ret));
     211
    225212        /* Disable kbd and aux */
    226213        wait_ready(dev);
     
    228215        wait_ready(dev);
    229216        pio_write_8(&dev->regs->data, i8042_KBD_DISABLE | i8042_AUX_DISABLE);
    230        
     217
    231218        /* Flush all current IO */
    232219        while (pio_read_8(&dev->regs->status) & i8042_OUTPUT_FULL)
    233220                (void) pio_read_8(&dev->regs->data);
    234221
     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];
    235240        memcpy(ranges, i8042_ranges, sizeof(i8042_ranges));
    236241        ranges[0].base = (uintptr_t) regs;
    237242
     243        const size_t cmd_count = sizeof(i8042_cmds) / sizeof(irq_cmd_t);
     244        irq_cmd_t cmds[cmd_count];
    238245        memcpy(cmds, i8042_cmds, sizeof(i8042_cmds));
    239246        cmds[0].addr = (void *) &(((i8042_regs_t *) regs)->status);
     
    246253                .cmds = cmds
    247254        };
    248        
    249         rc = register_interrupt_handler(ddf_dev, irq_kbd, i8042_irq_handler,
     255        ret = register_interrupt_handler(ddf_dev, irq_kbd, i8042_irq_handler,
    250256            &irq_code);
    251         if (rc != EOK) {
    252                 ddf_msg(LVL_ERROR, "Failed set handler for kbd: %s.",
    253                     ddf_dev_get_name(ddf_dev));
    254                 goto error;
    255         }
    256        
    257         rc = register_interrupt_handler(ddf_dev, irq_mouse, i8042_irq_handler,
     257        CHECK_RET_UNBIND_DESTROY(ret,
     258            "Failed set handler for kbd: %s.", str_error(ret));
     259
     260        ret = register_interrupt_handler(ddf_dev, irq_mouse, i8042_irq_handler,
    258261            &irq_code);
    259         if (rc != EOK) {
    260                 ddf_msg(LVL_ERROR, "Failed set handler for mouse: %s.",
    261                     ddf_dev_get_name(ddf_dev));
    262                 goto error;
    263         }
    264        
     262        CHECK_RET_UNBIND_DESTROY(ret,
     263            "Failed set handler for mouse: %s.", str_error(ret));
     264
    265265        /* Enable interrupts */
    266         async_sess_t *parent_sess = ddf_dev_parent_sess_get(ddf_dev);
    267         assert(parent_sess != NULL);
    268        
     266        async_sess_t *parent_sess =
     267            devman_parent_device_connect(EXCHANGE_SERIALIZE, ddf_dev->handle,
     268            IPC_FLAG_BLOCKING);
     269        ret = parent_sess ? EOK : ENOMEM;
     270        CHECK_RET_UNBIND_DESTROY(ret, "Failed to create parent connection.");
     271
    269272        const bool enabled = hw_res_enable_interrupt(parent_sess);
    270         if (!enabled) {
    271                 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to enable interrupts: %s.",
    272                     ddf_dev_get_name(ddf_dev));
    273                 rc = EIO;
    274                 goto error;
    275         }
    276        
     273        async_hangup(parent_sess);
     274        ret = enabled ? EOK : EIO;
     275        CHECK_RET_UNBIND_DESTROY(ret, "Failed to enable interrupts: %s.");
     276
    277277        /* Enable port interrupts. */
    278278        wait_ready(dev);
     
    281281        pio_write_8(&dev->regs->data, i8042_KBD_IE | i8042_KBD_TRANSLATE |
    282282            i8042_AUX_IE);
    283        
     283
    284284        return EOK;
    285 error:
    286         if (kbd_bound)
    287                 ddf_fun_unbind(dev->kbd_fun);
    288         if (aux_bound)
    289                 ddf_fun_unbind(dev->aux_fun);
    290         if (dev->kbd_fun != NULL)
    291                 ddf_fun_destroy(dev->kbd_fun);
    292         if (dev->aux_fun != NULL)
    293                 ddf_fun_destroy(dev->aux_fun);
    294 
    295         return rc;
    296 }
    297 
    298 // FIXME TODO use shared instead this
     285}
     286
     287// TODO use shared instead this
    299288enum {
    300289        IPC_CHAR_READ = DEV_FIRST_CUSTOM_METHOD,
     
    303292
    304293/** Write data to i8042 port.
    305  *
    306  * @param fun    DDF function.
     294 * @param fun DDF function.
    307295 * @param buffer Data source.
    308  * @param size   Data size.
    309  *
     296 * @param size Data size.
    310297 * @return Bytes written.
    311  *
    312298 */
    313299static int i8042_write(ddf_fun_t *fun, char *buffer, size_t size)
    314300{
    315         i8042_t *controller = dev_i8042(ddf_fun_get_dev(fun));
     301        assert(fun);
     302        assert(fun->driver_data);
     303        i8042_t *controller = fun->driver_data;
    316304        fibril_mutex_lock(&controller->write_guard);
    317        
    318305        for (size_t i = 0; i < size; ++i) {
    319                 if (controller->aux_fun == fun) {
    320                         wait_ready(controller);
    321                         pio_write_8(&controller->regs->status,
    322                             i8042_CMD_WRITE_AUX);
    323                 }
    324                
    325306                wait_ready(controller);
     307                if (controller->aux_fun == fun)
     308                        pio_write_8(
     309                            &controller->regs->status, i8042_CMD_WRITE_AUX);
    326310                pio_write_8(&controller->regs->data, buffer[i]);
    327311        }
    328        
    329312        fibril_mutex_unlock(&controller->write_guard);
    330313        return size;
     
    332315
    333316/** Read data from i8042 port.
    334  *
    335  * @param fun    DDF function.
     317 * @param fun DDF function.
    336318 * @param buffer Data place.
    337  * @param size   Data place size.
    338  *
     319 * @param size Data place size.
    339320 * @return Bytes read.
    340  *
    341321 */
    342322static int i8042_read(ddf_fun_t *fun, char *data, size_t size)
    343323{
    344         i8042_t *controller = dev_i8042(ddf_fun_get_dev(fun));
     324        assert(fun);
     325        assert(fun->driver_data);
     326
     327        i8042_t *controller = fun->driver_data;
    345328        buffer_t *buffer = (fun == controller->aux_fun) ?
    346329            &controller->aux_buffer : &controller->kbd_buffer;
    347        
    348         for (size_t i = 0; i < size; ++i)
     330        for (size_t i = 0; i < size; ++i) {
    349331                *data++ = buffer_read(buffer);
    350        
     332        }
    351333        return size;
    352334}
    353335
    354336/** Handle data requests.
    355  *
    356  * @param fun  ddf_fun_t function.
    357  * @param id   callid
     337 * @param fun ddf_fun_t function.
     338 * @param id callid
    358339 * @param call IPC request.
    359  *
    360340 */
    361341void default_handler(ddf_fun_t *fun, ipc_callid_t id, ipc_call_t *call)
     
    363343        const sysarg_t method = IPC_GET_IMETHOD(*call);
    364344        const size_t size = IPC_GET_ARG1(*call);
    365        
    366345        switch (method) {
    367346        case IPC_CHAR_READ:
    368347                if (size <= 4 * sizeof(sysarg_t)) {
    369348                        sysarg_t message[4] = {};
    370                        
    371                         i8042_read(fun, (char *) message, size);
     349                        i8042_read(fun, (char*)message, size);
    372350                        async_answer_4(id, size, message[0], message[1],
    373351                            message[2], message[3]);
    374                 } else
     352                } else {
    375353                        async_answer_0(id, ELIMIT);
     354                }
    376355                break;
    377        
     356
    378357        case IPC_CHAR_WRITE:
    379358                if (size <= 3 * sizeof(sysarg_t)) {
    380359                        const sysarg_t message[3] = {
    381                                 IPC_GET_ARG2(*call),
    382                                 IPC_GET_ARG3(*call),
    383                                 IPC_GET_ARG4(*call)
    384                         };
    385                        
    386                         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);
    387363                        async_answer_0(id, size);
    388                 } else
     364                } else {
    389365                        async_answer_0(id, ELIMIT);
    390        
     366                }
     367
    391368        default:
    392369                async_answer_0(id, EINVAL);
    393370        }
    394371}
    395 
    396372/**
    397373 * @}
Note: See TracChangeset for help on using the changeset viewer.