Ignore:
File:
1 edited

Legend:

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

    rcccdb8b7 rca4730a5  
    2929 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    3030 */
     31
    3132/** @addtogroup kbd_port
    3233 * @ingroup kbd
    3334 * @{
    3435 */
     36
    3537/** @file
    3638 * @brief i8042 PS/2 port driver.
    3739 */
    3840
    39 #include <devman.h>
    4041#include <device/hw_res.h>
    4142#include <ddi.h>
    42 #include <libarch/ddi.h>
    4343#include <errno.h>
    4444#include <str_error.h>
    4545#include <inttypes.h>
    46 
    4746#include <ddf/log.h>
    4847#include <ddf/interrupt.h>
    49 
    5048#include "i8042.h"
    5149
    52 #define NAME       "i8042"
     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 */
    5365
    5466void default_handler(ddf_fun_t *, ipc_callid_t, ipc_call_t *);
     
    5971};
    6072
    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 
    7773static const irq_pio_range_t i8042_ranges[] = {
    7874        {
     
    8682        {
    8783                .cmd = CMD_PIO_READ_8,
    88                 .addr = NULL,   /* will be patched in run-time */
     84                .addr = NULL,  /* will be patched in run-time */
    8985                .dstarg = 1
    9086        },
    9187        {
    92                 .cmd = CMD_BTEST,
     88                .cmd = CMD_AND,
    9389                .value = i8042_OUTPUT_FULL,
    9490                .srcarg = 1,
     
    10298        {
    10399                .cmd = CMD_PIO_READ_8,
    104                 .addr = NULL,   /* will be patched in run-time */
     100                .addr = NULL,  /* will be patched in run-time */
    105101                .dstarg = 2
    106102        },
     
    110106};
    111107
     108/** Get i8042 soft state from device node. */
     109static i8042_t *dev_i8042(ddf_dev_t *dev)
     110{
     111        return ddf_dev_data_get(dev);
     112}
     113
    112114/** Wait until it is safe to write to the device. */
    113115static void wait_ready(i8042_t *dev)
     
    118120
    119121/** Interrupt handler routine.
    120  * Writes new data to the corresponding buffer.
    121  * @param dev Device that caued the interrupt.
    122  * @param iid Call id.
     122 *
     123 * Write new data to the corresponding buffer.
     124 *
     125 * @param dev  Device that caued the interrupt.
     126 * @param iid  Call id.
    123127 * @param call pointerr to call data.
    124  */
    125 static 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 
     128 *
     129 */
     130static 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       
    132135        const uint8_t status = IPC_GET_ARG1(*call);
    133136        const uint8_t data = IPC_GET_ARG2(*call);
     137       
    134138        buffer_t *buffer = (status & i8042_AUX_DATA) ?
    135139            &controller->aux_buffer : &controller->kbd_buffer;
     140       
    136141        buffer_write(buffer, data);
    137142}
    138143
    139144/** Initialize i8042 driver structure.
    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.
     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.
    144150 * @param irq_mouse IRQ for aux port.
    145  * @param ddf_dev DDF device structure of the device.
     151 * @param ddf_dev   DDF device structure of the device.
     152 *
    146153 * @return Error code.
     154 *
    147155 */
    148156int i8042_init(i8042_t *dev, void *regs, size_t reg_size, int irq_kbd,
    149157    int irq_mouse, ddf_dev_t *ddf_dev)
    150158{
    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 
     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       
    160182        dev->kbd_fun = ddf_fun_create(ddf_dev, fun_inner, "ps2a");
    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 
     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       
    169192        dev->aux_fun = ddf_fun_create(ddf_dev, fun_inner, "ps2b");
    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 
     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       
    187205        buffer_init(&dev->kbd_buffer, dev->kbd_data, BUFFER_SIZE);
    188206        buffer_init(&dev->aux_buffer, dev->aux_data, BUFFER_SIZE);
    189207        fibril_mutex_initialize(&dev->write_guard);
    190 
    191 #define CHECK_RET_DESTROY(ret, msg...) \
    192 if  (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 
     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       
    212225        /* Disable kbd and aux */
    213226        wait_ready(dev);
     
    215228        wait_ready(dev);
    216229        pio_write_8(&dev->regs->data, i8042_KBD_DISABLE | i8042_AUX_DISABLE);
    217 
     230       
    218231        /* Flush all current IO */
    219232        while (pio_read_8(&dev->regs->status) & i8042_OUTPUT_FULL)
    220233                (void) pio_read_8(&dev->regs->data);
    221234
    222 #define CHECK_RET_UNBIND_DESTROY(ret, msg...) \
    223 if  (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];
    240235        memcpy(ranges, i8042_ranges, sizeof(i8042_ranges));
    241236        ranges[0].base = (uintptr_t) regs;
    242237
    243         const size_t cmd_count = sizeof(i8042_cmds) / sizeof(irq_cmd_t);
    244         irq_cmd_t cmds[cmd_count];
    245238        memcpy(cmds, i8042_cmds, sizeof(i8042_cmds));
    246239        cmds[0].addr = (void *) &(((i8042_regs_t *) regs)->status);
     
    253246                .cmds = cmds
    254247        };
    255         ret = register_interrupt_handler(ddf_dev, irq_kbd, i8042_irq_handler,
     248       
     249        rc = register_interrupt_handler(ddf_dev, irq_kbd, i8042_irq_handler,
    256250            &irq_code);
    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,
     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,
    261258            &irq_code);
    262         CHECK_RET_UNBIND_DESTROY(ret,
    263             "Failed set handler for mouse: %s.", str_error(ret));
    264 
     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       
    265265        /* Enable interrupts */
    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 
     266        async_sess_t *parent_sess = ddf_dev_parent_sess_get(ddf_dev);
     267        assert(parent_sess != NULL);
     268       
    272269        const bool enabled = hw_res_enable_interrupt(parent_sess);
    273         async_hangup(parent_sess);
    274         ret = enabled ? EOK : EIO;
    275         CHECK_RET_UNBIND_DESTROY(ret, "Failed to enable interrupts: %s.");
    276 
     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       
    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 }
    286 
    287 // TODO use shared instead this
     285error:
     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
    288299enum {
    289300        IPC_CHAR_READ = DEV_FIRST_CUSTOM_METHOD,
     
    292303
    293304/** Write data to i8042 port.
    294  * @param fun DDF function.
     305 *
     306 * @param fun    DDF function.
    295307 * @param buffer Data source.
    296  * @param size Data size.
     308 * @param size   Data size.
     309 *
    297310 * @return Bytes written.
     311 *
    298312 */
    299313static int i8042_write(ddf_fun_t *fun, char *buffer, size_t size)
    300314{
    301         assert(fun);
    302         assert(fun->driver_data);
    303         i8042_t *controller = fun->driver_data;
     315        i8042_t *controller = dev_i8042(ddf_fun_get_dev(fun));
    304316        fibril_mutex_lock(&controller->write_guard);
     317       
    305318        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               
    306325                wait_ready(controller);
    307                 if (controller->aux_fun == fun)
    308                         pio_write_8(
    309                             &controller->regs->status, i8042_CMD_WRITE_AUX);
    310326                pio_write_8(&controller->regs->data, buffer[i]);
    311327        }
     328       
    312329        fibril_mutex_unlock(&controller->write_guard);
    313330        return size;
     
    315332
    316333/** Read data from i8042 port.
    317  * @param fun DDF function.
     334 *
     335 * @param fun    DDF function.
    318336 * @param buffer Data place.
    319  * @param size Data place size.
     337 * @param size   Data place size.
     338 *
    320339 * @return Bytes read.
     340 *
    321341 */
    322342static int i8042_read(ddf_fun_t *fun, char *data, size_t size)
    323343{
    324         assert(fun);
    325         assert(fun->driver_data);
    326 
    327         i8042_t *controller = fun->driver_data;
     344        i8042_t *controller = dev_i8042(ddf_fun_get_dev(fun));
    328345        buffer_t *buffer = (fun == controller->aux_fun) ?
    329346            &controller->aux_buffer : &controller->kbd_buffer;
    330         for (size_t i = 0; i < size; ++i) {
     347       
     348        for (size_t i = 0; i < size; ++i)
    331349                *data++ = buffer_read(buffer);
    332         }
     350       
    333351        return size;
    334352}
    335353
    336354/** Handle data requests.
    337  * @param fun ddf_fun_t function.
    338  * @param id callid
     355 *
     356 * @param fun  ddf_fun_t function.
     357 * @param id   callid
    339358 * @param call IPC request.
     359 *
    340360 */
    341361void default_handler(ddf_fun_t *fun, ipc_callid_t id, ipc_call_t *call)
     
    343363        const sysarg_t method = IPC_GET_IMETHOD(*call);
    344364        const size_t size = IPC_GET_ARG1(*call);
     365       
    345366        switch (method) {
    346367        case IPC_CHAR_READ:
    347368                if (size <= 4 * sizeof(sysarg_t)) {
    348369                        sysarg_t message[4] = {};
    349                         i8042_read(fun, (char*)message, size);
     370                       
     371                        i8042_read(fun, (char *) message, size);
    350372                        async_answer_4(id, size, message[0], message[1],
    351373                            message[2], message[3]);
    352                 } else {
     374                } else
    353375                        async_answer_0(id, ELIMIT);
    354                 }
    355376                break;
    356 
     377       
    357378        case IPC_CHAR_WRITE:
    358379                if (size <= 3 * sizeof(sysarg_t)) {
    359380                        const sysarg_t message[3] = {
    360                                 IPC_GET_ARG2(*call), IPC_GET_ARG3(*call),
    361                                 IPC_GET_ARG4(*call) };
    362                         i8042_write(fun, (char*)message, size);
     381                                IPC_GET_ARG2(*call),
     382                                IPC_GET_ARG3(*call),
     383                                IPC_GET_ARG4(*call)
     384                        };
     385                       
     386                        i8042_write(fun, (char *) message, size);
    363387                        async_answer_0(id, size);
    364                 } else {
     388                } else
    365389                        async_answer_0(id, ELIMIT);
    366                 }
    367 
     390       
    368391        default:
    369392                async_answer_0(id, EINVAL);
    370393        }
    371394}
     395
    372396/**
    373397 * @}
Note: See TracChangeset for help on using the changeset viewer.