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