Changes in uspace/drv/char/i8042/i8042.c [876f6463:cccdb8b7] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/drv/char/i8042/i8042.c
r876f6463 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. … … 46 44 #include <str_error.h> 47 45 #include <inttypes.h> 46 48 47 #include <ddf/log.h> 49 48 #include <ddf/interrupt.h> 49 50 50 #include "i8042.h" 51 51 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" 99 53 100 54 void default_handler(ddf_fun_t *, ipc_callid_t, ipc_call_t *); … … 105 59 }; 106 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 77 static const irq_pio_range_t i8042_ranges[] = { 78 { 79 .base = 0, 80 .size = sizeof(i8042_regs_t) 81 } 82 }; 83 107 84 /** i8042 Interrupt pseudo-code. */ 108 85 static const irq_cmd_t i8042_cmds[] = { 109 86 { 110 87 .cmd = CMD_PIO_READ_8, 111 .addr = NULL, 88 .addr = NULL, /* will be patched in run-time */ 112 89 .dstarg = 1 113 90 }, … … 125 102 { 126 103 .cmd = CMD_PIO_READ_8, 127 .addr = NULL, 104 .addr = NULL, /* will be patched in run-time */ 128 105 .dstarg = 2 129 106 }, … … 141 118 142 119 /** 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. 148 123 * @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 */ 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) 155 129 return; 156 157 130 i8042_t *controller = dev->driver_data; 158 131 159 132 const uint8_t status = IPC_GET_ARG1(*call); 160 133 const uint8_t data = IPC_GET_ARG2(*call); 161 162 134 buffer_t *buffer = (status & i8042_AUX_DATA) ? 163 135 &controller->aux_buffer : &controller->kbd_buffer; 164 165 136 buffer_write(buffer, data); 166 137 } 167 138 168 139 /** 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. 174 144 * @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. 177 146 * @return Error code. 178 *179 147 */ 180 148 int i8042_init(i8042_t *dev, void *regs, size_t reg_size, int irq_kbd, … … 183 151 assert(ddf_dev); 184 152 assert(dev); 185 153 186 154 if (reg_size < sizeof(i8042_regs_t)) 187 155 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) 190 158 return -1; 191 159 192 160 dev->kbd_fun = ddf_fun_create(ddf_dev, fun_inner, "ps2a"); 193 161 if (!dev->kbd_fun) 194 162 return ENOMEM; 195 196 163 int ret = ddf_fun_add_match_id(dev->kbd_fun, "char/xtkbd", 90); 197 164 if (ret != EOK) { … … 199 166 return ret; 200 167 } 201 168 202 169 dev->aux_fun = ddf_fun_create(ddf_dev, fun_inner, "ps2b"); 203 170 if (!dev->aux_fun) { … … 205 172 return ENOMEM; 206 173 } 207 174 208 175 ret = ddf_fun_add_match_id(dev->aux_fun, "char/ps2mouse", 90); 209 176 if (ret != EOK) { … … 212 179 return ret; 213 180 } 214 181 215 182 dev->kbd_fun->ops = &ops; 216 183 dev->aux_fun->ops = &ops; 217 184 dev->kbd_fun->driver_data = dev; 218 185 dev->aux_fun->driver_data = dev; 219 186 220 187 buffer_init(&dev->kbd_buffer, dev->kbd_data, BUFFER_SIZE); 221 188 buffer_init(&dev->aux_buffer, dev->aux_data, BUFFER_SIZE); 222 189 fibril_mutex_initialize(&dev->write_guard); 223 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 224 204 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 228 208 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 232 212 /* Disable kbd and aux */ 233 213 wait_ready(dev); … … 235 215 wait_ready(dev); 236 216 pio_write_8(&dev->regs->data, i8042_KBD_DISABLE | i8042_AUX_DISABLE); 237 217 238 218 /* Flush all current IO */ 239 219 while (pio_read_8(&dev->regs->status) & i8042_OUTPUT_FULL) 240 220 (void) pio_read_8(&dev->regs->data); 241 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]; 240 memcpy(ranges, i8042_ranges, sizeof(i8042_ranges)); 241 ranges[0].base = (uintptr_t) regs; 242 242 243 const size_t cmd_count = sizeof(i8042_cmds) / sizeof(irq_cmd_t); 243 244 irq_cmd_t cmds[cmd_count]; 244 245 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 248 249 irq_code_t irq_code = { 250 .rangecount = range_count, 251 .ranges = ranges, 249 252 .cmdcount = cmd_count, 250 253 .cmds = cmds 251 254 }; 252 253 255 ret = register_interrupt_handler(ddf_dev, irq_kbd, i8042_irq_handler, 254 256 &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 258 260 ret = register_interrupt_handler(ddf_dev, irq_mouse, i8042_irq_handler, 259 261 &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 263 265 /* Enable interrupts */ 264 266 async_sess_t *parent_sess = … … 267 269 ret = parent_sess ? EOK : ENOMEM; 268 270 CHECK_RET_UNBIND_DESTROY(ret, "Failed to create parent connection."); 269 271 270 272 const bool enabled = hw_res_enable_interrupt(parent_sess); 271 273 async_hangup(parent_sess); 272 274 ret = enabled ? EOK : EIO; 273 275 CHECK_RET_UNBIND_DESTROY(ret, "Failed to enable interrupts: %s."); 274 276 275 277 /* Enable port interrupts. */ 276 278 wait_ready(dev); … … 279 281 pio_write_8(&dev->regs->data, i8042_KBD_IE | i8042_KBD_TRANSLATE | 280 282 i8042_AUX_IE); 281 283 282 284 return EOK; 283 285 } 284 286 285 // FIXMETODO use shared instead this287 // TODO use shared instead this 286 288 enum { 287 289 IPC_CHAR_READ = DEV_FIRST_CUSTOM_METHOD, … … 290 292 291 293 /** Write data to i8042 port. 292 * 293 * @param fun DDF function. 294 * @param fun DDF function. 294 295 * @param buffer Data source. 295 * @param size Data size. 296 * 296 * @param size Data size. 297 297 * @return Bytes written. 298 *299 298 */ 300 299 static int i8042_write(ddf_fun_t *fun, char *buffer, size_t size) … … 302 301 assert(fun); 303 302 assert(fun->driver_data); 304 305 303 i8042_t *controller = fun->driver_data; 306 304 fibril_mutex_lock(&controller->write_guard); 307 308 305 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 315 306 wait_ready(controller); 307 if (controller->aux_fun == fun) 308 pio_write_8( 309 &controller->regs->status, i8042_CMD_WRITE_AUX); 316 310 pio_write_8(&controller->regs->data, buffer[i]); 317 311 } 318 319 312 fibril_mutex_unlock(&controller->write_guard); 320 313 return size; … … 322 315 323 316 /** Read data from i8042 port. 324 * 325 * @param fun DDF function. 317 * @param fun DDF function. 326 318 * @param buffer Data place. 327 * @param size Data place size. 328 * 319 * @param size Data place size. 329 320 * @return Bytes read. 330 *331 321 */ 332 322 static int i8042_read(ddf_fun_t *fun, char *data, size_t size) … … 334 324 assert(fun); 335 325 assert(fun->driver_data); 336 326 337 327 i8042_t *controller = fun->driver_data; 338 328 buffer_t *buffer = (fun == controller->aux_fun) ? 339 329 &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) { 342 331 *data++ = buffer_read(buffer); 343 332 } 344 333 return size; 345 334 } 346 335 347 336 /** 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 351 339 * @param call IPC request. 352 *353 340 */ 354 341 void default_handler(ddf_fun_t *fun, ipc_callid_t id, ipc_call_t *call) … … 356 343 const sysarg_t method = IPC_GET_IMETHOD(*call); 357 344 const size_t size = IPC_GET_ARG1(*call); 358 359 345 switch (method) { 360 346 case IPC_CHAR_READ: 361 347 if (size <= 4 * sizeof(sysarg_t)) { 362 348 sysarg_t message[4] = {}; 363 364 i8042_read(fun, (char *) message, size); 349 i8042_read(fun, (char*)message, size); 365 350 async_answer_4(id, size, message[0], message[1], 366 351 message[2], message[3]); 367 } else 352 } else { 368 353 async_answer_0(id, ELIMIT); 354 } 369 355 break; 370 356 371 357 case IPC_CHAR_WRITE: 372 358 if (size <= 3 * sizeof(sysarg_t)) { 373 359 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); 380 363 async_answer_0(id, size); 381 } else 364 } else { 382 365 async_answer_0(id, ELIMIT); 383 366 } 367 384 368 default: 385 369 async_answer_0(id, EINVAL); 386 370 } 387 371 } 388 389 372 /** 390 373 * @}
Note:
See TracChangeset
for help on using the changeset viewer.