Changes in uspace/drv/char/i8042/i8042.c [ca4730a5:cccdb8b7] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/drv/char/i8042/i8042.c
rca4730a5 rcccdb8b7 29 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 30 */ 31 32 31 /** @addtogroup kbd_port 33 32 * @ingroup kbd 34 33 * @{ 35 34 */ 36 37 35 /** @file 38 36 * @brief i8042 PS/2 port driver. 39 37 */ 40 38 39 #include <devman.h> 41 40 #include <device/hw_res.h> 42 41 #include <ddi.h> 42 #include <libarch/ddi.h> 43 43 #include <errno.h> 44 44 #include <str_error.h> 45 45 #include <inttypes.h> 46 46 47 #include <ddf/log.h> 47 48 #include <ddf/interrupt.h> 49 48 50 #include "i8042.h" 49 51 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" 65 53 66 54 void default_handler(ddf_fun_t *, ipc_callid_t, ipc_call_t *); … … 71 59 }; 72 60 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 73 77 static const irq_pio_range_t i8042_ranges[] = { 74 78 { … … 82 86 { 83 87 .cmd = CMD_PIO_READ_8, 84 .addr = NULL, 88 .addr = NULL, /* will be patched in run-time */ 85 89 .dstarg = 1 86 90 }, 87 91 { 88 .cmd = CMD_ AND,92 .cmd = CMD_BTEST, 89 93 .value = i8042_OUTPUT_FULL, 90 94 .srcarg = 1, … … 98 102 { 99 103 .cmd = CMD_PIO_READ_8, 100 .addr = NULL, 104 .addr = NULL, /* will be patched in run-time */ 101 105 .dstarg = 2 102 106 }, … … 106 110 }; 107 111 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 114 112 /** Wait until it is safe to write to the device. */ 115 113 static void wait_ready(i8042_t *dev) … … 120 118 121 119 /** 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. 127 123 * @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 */ 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 135 132 const uint8_t status = IPC_GET_ARG1(*call); 136 133 const uint8_t data = IPC_GET_ARG2(*call); 137 138 134 buffer_t *buffer = (status & i8042_AUX_DATA) ? 139 135 &controller->aux_buffer : &controller->kbd_buffer; 140 141 136 buffer_write(buffer, data); 142 137 } 143 138 144 139 /** 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. 150 144 * @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. 153 146 * @return Error code. 154 *155 147 */ 156 148 int i8042_init(i8042_t *dev, void *regs, size_t reg_size, int irq_kbd, 157 149 int irq_mouse, ddf_dev_t *ddf_dev) 158 150 { 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 182 160 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 192 169 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 205 187 buffer_init(&dev->kbd_buffer, dev->kbd_data, BUFFER_SIZE); 206 188 buffer_init(&dev->aux_buffer, dev->aux_data, BUFFER_SIZE); 207 189 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...) \ 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 225 212 /* Disable kbd and aux */ 226 213 wait_ready(dev); … … 228 215 wait_ready(dev); 229 216 pio_write_8(&dev->regs->data, i8042_KBD_DISABLE | i8042_AUX_DISABLE); 230 217 231 218 /* Flush all current IO */ 232 219 while (pio_read_8(&dev->regs->status) & i8042_OUTPUT_FULL) 233 220 (void) pio_read_8(&dev->regs->data); 234 221 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]; 235 240 memcpy(ranges, i8042_ranges, sizeof(i8042_ranges)); 236 241 ranges[0].base = (uintptr_t) regs; 237 242 243 const size_t cmd_count = sizeof(i8042_cmds) / sizeof(irq_cmd_t); 244 irq_cmd_t cmds[cmd_count]; 238 245 memcpy(cmds, i8042_cmds, sizeof(i8042_cmds)); 239 246 cmds[0].addr = (void *) &(((i8042_regs_t *) regs)->status); … … 246 253 .cmds = cmds 247 254 }; 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, 250 256 &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, 258 261 &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 265 265 /* 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 269 272 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 277 277 /* Enable port interrupts. */ 278 278 wait_ready(dev); … … 281 281 pio_write_8(&dev->regs->data, i8042_KBD_IE | i8042_KBD_TRANSLATE | 282 282 i8042_AUX_IE); 283 283 284 284 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 299 288 enum { 300 289 IPC_CHAR_READ = DEV_FIRST_CUSTOM_METHOD, … … 303 292 304 293 /** Write data to i8042 port. 305 * 306 * @param fun DDF function. 294 * @param fun DDF function. 307 295 * @param buffer Data source. 308 * @param size Data size. 309 * 296 * @param size Data size. 310 297 * @return Bytes written. 311 *312 298 */ 313 299 static int i8042_write(ddf_fun_t *fun, char *buffer, size_t size) 314 300 { 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; 316 304 fibril_mutex_lock(&controller->write_guard); 317 318 305 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 325 306 wait_ready(controller); 307 if (controller->aux_fun == fun) 308 pio_write_8( 309 &controller->regs->status, i8042_CMD_WRITE_AUX); 326 310 pio_write_8(&controller->regs->data, buffer[i]); 327 311 } 328 329 312 fibril_mutex_unlock(&controller->write_guard); 330 313 return size; … … 332 315 333 316 /** Read data from i8042 port. 334 * 335 * @param fun DDF function. 317 * @param fun DDF function. 336 318 * @param buffer Data place. 337 * @param size Data place size. 338 * 319 * @param size Data place size. 339 320 * @return Bytes read. 340 *341 321 */ 342 322 static int i8042_read(ddf_fun_t *fun, char *data, size_t size) 343 323 { 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; 345 328 buffer_t *buffer = (fun == controller->aux_fun) ? 346 329 &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) { 349 331 *data++ = buffer_read(buffer); 350 332 } 351 333 return size; 352 334 } 353 335 354 336 /** 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 358 339 * @param call IPC request. 359 *360 340 */ 361 341 void default_handler(ddf_fun_t *fun, ipc_callid_t id, ipc_call_t *call) … … 363 343 const sysarg_t method = IPC_GET_IMETHOD(*call); 364 344 const size_t size = IPC_GET_ARG1(*call); 365 366 345 switch (method) { 367 346 case IPC_CHAR_READ: 368 347 if (size <= 4 * sizeof(sysarg_t)) { 369 348 sysarg_t message[4] = {}; 370 371 i8042_read(fun, (char *) message, size); 349 i8042_read(fun, (char*)message, size); 372 350 async_answer_4(id, size, message[0], message[1], 373 351 message[2], message[3]); 374 } else 352 } else { 375 353 async_answer_0(id, ELIMIT); 354 } 376 355 break; 377 356 378 357 case IPC_CHAR_WRITE: 379 358 if (size <= 3 * sizeof(sysarg_t)) { 380 359 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); 387 363 async_answer_0(id, size); 388 } else 364 } else { 389 365 async_answer_0(id, ELIMIT); 390 366 } 367 391 368 default: 392 369 async_answer_0(id, EINVAL); 393 370 } 394 371 } 395 396 372 /** 397 373 * @}
Note:
See TracChangeset
for help on using the changeset viewer.