Changeset cfb79747 in mainline for uspace/drv/char/i8042/i8042.c
- Timestamp:
- 2012-02-14T22:06:15Z (12 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- a31aad1
- Parents:
- 199112e4 (diff), e10d41a (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/drv/char/i8042/i8042.c
r199112e4 rcfb79747 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. … … 44 46 #include <str_error.h> 45 47 #include <inttypes.h> 46 47 48 #include <ddf/log.h> 48 49 #include <ddf/interrupt.h> 49 50 50 #include "i8042.h" 51 51 52 #define NAME "i8042" 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) 53 99 54 100 void default_handler(ddf_fun_t *, ipc_callid_t, ipc_call_t *); … … 59 105 }; 60 106 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 107 static const irq_pio_range_t i8042_ranges[] = { 78 108 { … … 86 116 { 87 117 .cmd = CMD_PIO_READ_8, 88 .addr = NULL, 118 .addr = NULL, /* will be patched in run-time */ 89 119 .dstarg = 1 90 120 }, … … 102 132 { 103 133 .cmd = CMD_PIO_READ_8, 104 .addr = NULL, 134 .addr = NULL, /* will be patched in run-time */ 105 135 .dstarg = 2 106 136 }, … … 118 148 119 149 /** Interrupt handler routine. 120 * Writes new data to the corresponding buffer. 121 * @param dev Device that caued the interrupt. 122 * @param iid Call id. 150 * 151 * Write new data to the corresponding buffer. 152 * 153 * @param dev Device that caued the interrupt. 154 * @param iid Call id. 123 155 * @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) 156 * 157 */ 158 static void i8042_irq_handler(ddf_dev_t *dev, ipc_callid_t iid, 159 ipc_call_t *call) 160 { 161 if ((!dev) || (!dev->driver_data)) 129 162 return; 163 130 164 i8042_t *controller = dev->driver_data; 131 165 132 166 const uint8_t status = IPC_GET_ARG1(*call); 133 167 const uint8_t data = IPC_GET_ARG2(*call); 168 134 169 buffer_t *buffer = (status & i8042_AUX_DATA) ? 135 170 &controller->aux_buffer : &controller->kbd_buffer; 171 136 172 buffer_write(buffer, data); 137 173 } 138 174 139 175 /** 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. 176 * 177 * @param dev Driver structure to initialize. 178 * @param regs I/O address of registers. 179 * @param reg_size size of the reserved I/O address space. 180 * @param irq_kbd IRQ for primary port. 144 181 * @param irq_mouse IRQ for aux port. 145 * @param ddf_dev DDF device structure of the device. 182 * @param ddf_dev DDF device structure of the device. 183 * 146 184 * @return Error code. 185 * 147 186 */ 148 187 int i8042_init(i8042_t *dev, void *regs, size_t reg_size, int irq_kbd, … … 151 190 assert(ddf_dev); 152 191 assert(dev); 153 192 154 193 if (reg_size < sizeof(i8042_regs_t)) 155 194 return EINVAL; 156 157 if (pio_enable(regs, sizeof(i8042_regs_t), (void **)&dev->regs) != 0)195 196 if (pio_enable(regs, sizeof(i8042_regs_t), (void **) &dev->regs) != 0) 158 197 return -1; 159 198 160 199 dev->kbd_fun = ddf_fun_create(ddf_dev, fun_inner, "ps2a"); 161 200 if (!dev->kbd_fun) 162 201 return ENOMEM; 202 163 203 int ret = ddf_fun_add_match_id(dev->kbd_fun, "char/xtkbd", 90); 164 204 if (ret != EOK) { … … 166 206 return ret; 167 207 } 168 208 169 209 dev->aux_fun = ddf_fun_create(ddf_dev, fun_inner, "ps2b"); 170 210 if (!dev->aux_fun) { … … 172 212 return ENOMEM; 173 213 } 174 214 175 215 ret = ddf_fun_add_match_id(dev->aux_fun, "char/ps2mouse", 90); 176 216 if (ret != EOK) { … … 179 219 return ret; 180 220 } 181 221 182 222 dev->kbd_fun->ops = &ops; 183 223 dev->aux_fun->ops = &ops; 184 224 dev->kbd_fun->driver_data = dev; 185 225 dev->aux_fun->driver_data = dev; 186 226 187 227 buffer_init(&dev->kbd_buffer, dev->kbd_data, BUFFER_SIZE); 188 228 buffer_init(&dev->aux_buffer, dev->aux_data, BUFFER_SIZE); 189 229 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 230 204 231 ret = ddf_fun_bind(dev->kbd_fun); 205 CHECK_RET_DESTROY(ret, 206 "Failed to bind keyboard function: %s.",str_error(ret));207 232 CHECK_RET_DESTROY(ret, "Failed to bind keyboard function: %s.", 233 str_error(ret)); 234 208 235 ret = ddf_fun_bind(dev->aux_fun); 209 CHECK_RET_DESTROY(ret, 210 "Failed to bind mouse function: %s.",str_error(ret));211 236 CHECK_RET_DESTROY(ret, "Failed to bind mouse function: %s.", 237 str_error(ret)); 238 212 239 /* Disable kbd and aux */ 213 240 wait_ready(dev); … … 215 242 wait_ready(dev); 216 243 pio_write_8(&dev->regs->data, i8042_KBD_DISABLE | i8042_AUX_DISABLE); 217 244 218 245 /* Flush all current IO */ 219 246 while (pio_read_8(&dev->regs->status) & i8042_OUTPUT_FULL) 220 247 (void) pio_read_8(&dev->regs->data); 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)0236 248 237 249 const size_t range_count = sizeof(i8042_ranges) / … … 253 265 .cmds = cmds 254 266 }; 267 255 268 ret = register_interrupt_handler(ddf_dev, irq_kbd, i8042_irq_handler, 256 269 &irq_code); 257 CHECK_RET_UNBIND_DESTROY(ret, 258 "Failed set handler for kbd: %s.",str_error(ret));259 270 CHECK_RET_UNBIND_DESTROY(ret, "Failed set handler for kbd: %s.", 271 str_error(ret)); 272 260 273 ret = register_interrupt_handler(ddf_dev, irq_mouse, i8042_irq_handler, 261 274 &irq_code); 262 CHECK_RET_UNBIND_DESTROY(ret, 263 "Failed set handler for mouse: %s.",str_error(ret));264 275 CHECK_RET_UNBIND_DESTROY(ret, "Failed set handler for mouse: %s.", 276 str_error(ret)); 277 265 278 /* Enable interrupts */ 266 279 async_sess_t *parent_sess = … … 269 282 ret = parent_sess ? EOK : ENOMEM; 270 283 CHECK_RET_UNBIND_DESTROY(ret, "Failed to create parent connection."); 271 284 272 285 const bool enabled = hw_res_enable_interrupt(parent_sess); 273 286 async_hangup(parent_sess); 274 287 ret = enabled ? EOK : EIO; 275 288 CHECK_RET_UNBIND_DESTROY(ret, "Failed to enable interrupts: %s."); 276 289 277 290 /* Enable port interrupts. */ 278 291 wait_ready(dev); … … 281 294 pio_write_8(&dev->regs->data, i8042_KBD_IE | i8042_KBD_TRANSLATE | 282 295 i8042_AUX_IE); 283 296 284 297 return EOK; 285 298 } 286 299 287 // TODO use shared instead this300 // FIXME TODO use shared instead this 288 301 enum { 289 302 IPC_CHAR_READ = DEV_FIRST_CUSTOM_METHOD, … … 292 305 293 306 /** Write data to i8042 port. 294 * @param fun DDF function. 307 * 308 * @param fun DDF function. 295 309 * @param buffer Data source. 296 * @param size Data size. 310 * @param size Data size. 311 * 297 312 * @return Bytes written. 313 * 298 314 */ 299 315 static int i8042_write(ddf_fun_t *fun, char *buffer, size_t size) … … 301 317 assert(fun); 302 318 assert(fun->driver_data); 319 303 320 i8042_t *controller = fun->driver_data; 304 321 fibril_mutex_lock(&controller->write_guard); 322 305 323 for (size_t i = 0; i < size; ++i) { 324 if (controller->aux_fun == fun) { 325 wait_ready(controller); 326 pio_write_8(&controller->regs->status, 327 i8042_CMD_WRITE_AUX); 328 } 329 306 330 wait_ready(controller); 307 if (controller->aux_fun == fun)308 pio_write_8(309 &controller->regs->status, i8042_CMD_WRITE_AUX);310 331 pio_write_8(&controller->regs->data, buffer[i]); 311 332 } 333 312 334 fibril_mutex_unlock(&controller->write_guard); 313 335 return size; … … 315 337 316 338 /** Read data from i8042 port. 317 * @param fun DDF function. 339 * 340 * @param fun DDF function. 318 341 * @param buffer Data place. 319 * @param size Data place size. 342 * @param size Data place size. 343 * 320 344 * @return Bytes read. 345 * 321 346 */ 322 347 static int i8042_read(ddf_fun_t *fun, char *data, size_t size) … … 324 349 assert(fun); 325 350 assert(fun->driver_data); 326 351 327 352 i8042_t *controller = fun->driver_data; 328 353 buffer_t *buffer = (fun == controller->aux_fun) ? 329 354 &controller->aux_buffer : &controller->kbd_buffer; 330 for (size_t i = 0; i < size; ++i) { 355 356 for (size_t i = 0; i < size; ++i) 331 357 *data++ = buffer_read(buffer); 332 }358 333 359 return size; 334 360 } 335 361 336 362 /** Handle data requests. 337 * @param fun ddf_fun_t function. 338 * @param id callid 363 * 364 * @param fun ddf_fun_t function. 365 * @param id callid 339 366 * @param call IPC request. 367 * 340 368 */ 341 369 void default_handler(ddf_fun_t *fun, ipc_callid_t id, ipc_call_t *call) … … 343 371 const sysarg_t method = IPC_GET_IMETHOD(*call); 344 372 const size_t size = IPC_GET_ARG1(*call); 373 345 374 switch (method) { 346 375 case IPC_CHAR_READ: 347 376 if (size <= 4 * sizeof(sysarg_t)) { 348 377 sysarg_t message[4] = {}; 349 i8042_read(fun, (char*)message, size); 378 379 i8042_read(fun, (char *) message, size); 350 380 async_answer_4(id, size, message[0], message[1], 351 381 message[2], message[3]); 352 } else {382 } else 353 383 async_answer_0(id, ELIMIT); 354 }355 384 break; 356 385 357 386 case IPC_CHAR_WRITE: 358 387 if (size <= 3 * sizeof(sysarg_t)) { 359 388 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); 389 IPC_GET_ARG2(*call), 390 IPC_GET_ARG3(*call), 391 IPC_GET_ARG4(*call) 392 }; 393 394 i8042_write(fun, (char *) message, size); 363 395 async_answer_0(id, size); 364 } else {396 } else 365 397 async_answer_0(id, ELIMIT); 366 } 367 398 368 399 default: 369 400 async_answer_0(id, EINVAL); 370 401 } 371 402 } 403 372 404 /** 373 405 * @}
Note:
See TracChangeset
for help on using the changeset viewer.