/**************************************************************************** * drivers/input/spq10kbd.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* This format is used to construct the /dev/kbd[n] device driver path. It * defined here so that it will be used consistently in all places. */ #define DEV_FORMAT "/dev/kbd%c" #define DEV_NAMELEN 11 /* Number of Joystick discretes */ #define DJOY_NGPIOS 5 /* Bitset of supported Joystick discretes */ #define DJOY_SUPPORTED (DJOY_UP_BIT | DJOY_DOWN_BIT | DJOY_LEFT_BIT | \ DJOY_RIGHT_BIT | DJOY_BUTTON_1_BIT | \ DJOY_BUTTON_2_BIT | DJOY_BUTTON_3_BIT | \ DJOY_BUTTON_4_BIT) /* Registers */ #define SPQ10KBD_VER 0x01 #define SPQ10KBD_CFG 0x02 #define SPQ10KBD_INT 0x03 #define SPQ10KBD_KEY 0x04 #define SPQ10KBD_BKL 0x05 #define SPQ10KBD_DEB 0x06 #define SPQ10KBD_FRQ 0x07 #define SPQ10KBD_RST 0x08 #define SPQ10KBD_FIF 0x09 /* VER */ #define SPQ10KBD_VER_MAJOR_SHIFT 4 #define SPQ10KBD_VER_MAJOR_MASK (0xf << SPQ10KBD_VER_MAJOR_SHIFT) #define SPQ10KBD_VER_MINOR_SHIFT 0 #define SPQ10KBD_VER_MINOR_MASK (0xf << SPQ10KBD_VER_MINOR_SHIFT) #define SPQ10KBD_VER_00_02 0x0002 /* CFG */ #define SPQ10KBD_CFG_OVERFLOW_ON (1 << 0) #define SPQ10KBD_CFG_OVERFLOW_INT (1 << 1) #define SPQ10KBD_CFG_CAPSLOCK_INT (1 << 2) #define SPQ10KBD_CFG_NUMLOCK_INT (1 << 3) #define SPQ10KBD_CFG_KEY_INT (1 << 4) #define SPQ10KBD_CFG_PANIC_INT (1 << 5) #define SPQ10KBD_CFG_REPORT_MODS (1 << 6) #define SPQ10KBD_CFG_USE_MODS (1 << 7) /* INT */ #define SPQ10KBD_INT_OVERFLOW (1 << 0) #define SPQ10KBD_INT_CAPSLOCK (1 << 1) #define SPQ10KBD_INT_NUMLOCK (1 << 2) #define SPQ10KBD_INT_KEY (1 << 3) #define SPQ10KBD_INT_PANIC (1 << 4) /* KEY */ #define SPQ10KBD_KEY_COUNT_SHIFT 0 #define SPQ10KBD_KEY_COUNT_MASK (0xf << SPQ10KBD_KEY_COUNT_SHIFT) #define SPQ10KBD_KEY_CAPSLOCK (1 << 5) #define SPQ10KBD_KEY_NUMLOCK (1 << 6) /* FIF */ #define SPQ10KBD_FIF_STATE_SHIFT 0 #define SPQ10KBD_FIF_STATE_MASK (0xff << SPQ10KBD_FIF_STATE_SHIFT) #define SPQ10KBD_FIF_KEY_SHIFT 8 #define SPQ10KBD_FIF_KEY_MASK (0xff << SPQ10KBD_FIF_KEY_SHIFT) #define KEY_PRESS 0x01 #define KEY_PRESS_HOLD 0x02 #define KEY_RELEASE 0x03 /* Special Key Encodings */ #define KEY_BUTTON_FIRST 0x01 /* Start of the button region */ #define KEY_JOY_UP 0x01 #define KEY_JOY_DOWN 0x02 #define KEY_JOY_LEFT 0x03 #define KEY_JOY_RIGHT 0x04 #define KEY_JOY_CENTER 0x05 #define KEY_BTN_LEFT1 0x06 #define KEY_BTN_RIGHT1 0x07 #define KEY_BACKSPACE 0x08 /* Normal ASCII */ #define KEY_TAB 0x09 /* Normal ASCII */ #define KEY_NL 0x0a /* Normal ASCII */ #define KEY_BTN_LEFT2 0x11 #define KEY_BTN_RIGHT2 0x12 #define KEY_BUTTON_LAST 0x12 /* End of the button region */ /* Key to joystick mapping */ #ifdef CONFIG_SPQ10KBD_DJOY static const djoy_buttonset_t joystick_map[] = { 0, /* No Key */ DJOY_UP_BIT, /* KEY_JOY_UP */ DJOY_DOWN_BIT, /* KEY_JOY_DOWN */ DJOY_LEFT_BIT, /* KEY_JOY_LEFT */ DJOY_RIGHT_BIT, /* KEY_JOY_RIGHT */ 0, /* KEY_JOY_CENTER */ DJOY_BUTTON_1_BIT, /* KEY_BTN_LEFT1 */ DJOY_BUTTON_3_BIT, /* KEY_BTN_RIGHT1 */ 0, /* KEY_BACKSPACE */ 0, /* KEY_TAB */ 0, /* KEY_NL */ 0, /* No Key */ 0, /* No Key */ 0, /* No Key */ 0, /* No Key */ 0, /* No Key */ 0, /* No Key */ DJOY_BUTTON_2_BIT, /* KEY_BTN_LEFT2 */ DJOY_BUTTON_4_BIT, /* KEY_BTN_RIGHT2 */ }; #endif /* CONFIG_SPQ10KBD_DJOY */ /**************************************************************************** * Private Types ****************************************************************************/ struct spq10kbd_dev_s { FAR const struct spq10kbd_config_s *config; /* Board configuration data */ FAR struct i2c_master_s *i2c; /* Saved I2C driver instance */ #ifdef CONFIG_SPQ10KBD_DJOY struct djoy_lowerhalf_s djoylower; /* Digital joystick */ djoy_interrupt_t djoyhandle; /* Joystick handler func */ FAR void *djoycbarg; /* Joystick callback arg */ djoy_buttonset_t djoypressmask; /* Joystick press evts */ djoy_buttonset_t djoyreleasemask; /* Joystick release evts */ djoy_buttonset_t djoystate; /* Joystick button state */ #endif /* CONFIG_SPQ10KBD_DJOY */ sem_t exclsem; /* Exclusive access to dev */ sem_t waitsem; /* Signal waiting thread */ bool waiting; /* Waiting for keyboard data */ struct work_s work; /* Supports the interrupt handling "bottom half" */ /* The following is a list if poll structures of threads waiting for * driver events. The 'struct pollfd' reference for each open is also * retained in the f_priv field of the 'struct file'. */ struct pollfd *fds[CONFIG_SPQ10KBD_NPOLLWAITERS]; /* Buffer used to collect and buffer incoming keyboard characters */ uint16_t headndx; /* Buffer head index */ uint16_t tailndx; /* Buffer tail index */ uint8_t kbdbuffer[CONFIG_SPQ10KBD_BUFSIZE]; uint8_t crefs; /* Reference count on the driver instance */ }; /**************************************************************************** * Static Function Prototypes ****************************************************************************/ static int spq10kbd_interrupt(int irq, FAR void *context, FAR void *arg); static void spq10kbd_worker(FAR void *arg); static void spq10kbd_putreg8(FAR struct spq10kbd_dev_s *priv, uint8_t regaddr, uint8_t regval); static uint8_t spq10kbd_getreg8(FAR struct spq10kbd_dev_s *priv, uint8_t regaddr); static uint16_t spq10kbd_getreg16(FAR struct spq10kbd_dev_s *priv, uint8_t regaddr); static int spq10kbd_checkver(FAR struct spq10kbd_dev_s *priv); static int spq10kbd_reset(FAR struct spq10kbd_dev_s *priv); static void spq10kbd_putbuffer(FAR struct spq10kbd_dev_s *priv, uint8_t keycode); /* Digital Joystick methods */ #ifdef CONFIG_SPQ10KBD_DJOY static djoy_buttonset_t djoy_supported( FAR const struct djoy_lowerhalf_s *lower); static djoy_buttonset_t djoy_sample( FAR const struct djoy_lowerhalf_s *lower); static void djoy_enable(FAR const struct djoy_lowerhalf_s *lower, djoy_buttonset_t press, djoy_buttonset_t release, djoy_interrupt_t handler, FAR void *arg); #endif /* CONFIG_SPQ10KBD_DJOY */ /* Driver methods. We export the keyboard as a standard character driver */ static int spq10kbd_open(FAR struct file *filep); static int spq10kbd_close(FAR struct file *filep); static ssize_t spq10kbd_read(FAR struct file *filep, FAR char *buffer, size_t len); static ssize_t spq10kbd_write(FAR struct file *filep, FAR const char *buffer, size_t len); static int spq10kbd_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup); /**************************************************************************** * Private Data ****************************************************************************/ static const struct file_operations g_hidkbd_fops = { spq10kbd_open, /* open */ spq10kbd_close, /* close */ spq10kbd_read, /* read */ spq10kbd_write, /* write */ NULL, /* seek */ NULL, /* ioctl */ spq10kbd_poll /* poll */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , NULL /* unlink */ #endif }; /**************************************************************************** * Private Functions ****************************************************************************/ #ifdef CONFIG_SPQ10KBD_DJOY /**************************************************************************** * Name: djoy_supported * * Description: * Return the set of buttons supported on the discrete joystick device * ****************************************************************************/ static djoy_buttonset_t djoy_supported( FAR const struct djoy_lowerhalf_s *lower) { iinfo("Supported: %02x\n", DJOY_SUPPORTED); return (djoy_buttonset_t)DJOY_SUPPORTED; } /**************************************************************************** * Name: djoy_sample * * Description: * Return the current state of all discrete joystick buttons * ****************************************************************************/ static djoy_buttonset_t djoy_sample( FAR const struct djoy_lowerhalf_s *lower) { FAR struct spq10kbd_dev_s *priv = (FAR struct spq10kbd_dev_s *)(lower->config); return priv->djoystate; } /**************************************************************************** * Name: djoy_enable * * Description: * Enable interrupts on the selected set of joystick buttons. An empty * set will disable all interrupts. * ****************************************************************************/ static void djoy_enable(FAR const struct djoy_lowerhalf_s *lower, djoy_buttonset_t press, djoy_buttonset_t release, djoy_interrupt_t handler, FAR void *arg) { FAR struct spq10kbd_dev_s *priv = (FAR struct spq10kbd_dev_s *)(lower->config); priv->djoypressmask = press; priv->djoyreleasemask = release; priv->djoyhandle = handler; priv->djoycbarg = arg; } #endif /* CONFIG_SPQ10KBD_DJOY */ /**************************************************************************** * Name: spq10kbd_worker ****************************************************************************/ static void spq10kbd_worker(FAR void *arg) { FAR struct spq10kbd_dev_s *priv = (FAR struct spq10kbd_dev_s *)arg; uint16_t regval; uint8_t key; uint8_t state; int ret; ret = nxsem_wait_uninterruptible(&priv->exclsem); if (ret < 0) { return; } regval = spq10kbd_getreg8(priv, SPQ10KBD_INT); if (regval & SPQ10KBD_INT_KEY) { /* There is a keypress in the FIFO */ while (spq10kbd_getreg8(priv, SPQ10KBD_KEY) & SPQ10KBD_KEY_COUNT_MASK) { regval = spq10kbd_getreg16(priv, SPQ10KBD_FIF); state = (regval & SPQ10KBD_FIF_STATE_MASK) >> \ SPQ10KBD_FIF_STATE_SHIFT; key = (regval & SPQ10KBD_FIF_KEY_MASK) >> SPQ10KBD_FIF_KEY_SHIFT; if (key <= KEY_BUTTON_LAST && !(key == KEY_BACKSPACE || key == KEY_TAB || key == KEY_NL)) { #ifdef CONFIG_SPQ10KBD_DJOY if (joystick_map[key] == 0) { /* Key is not mapped, skip */ iinfo("Skipping unmapped key %02x\n", key); } switch (state) { case KEY_PRESS: iinfo("Button Press: %02x\n", key); priv->djoystate |= joystick_map[key]; if (priv->djoypressmask & joystick_map[key]) { priv->djoyhandle(&priv->djoylower, priv->djoycbarg); } break; case KEY_RELEASE: iinfo("Button Release: %02x\n", key); priv->djoystate &= ~joystick_map[key]; if (priv->djoypressmask & joystick_map[key]) { priv->djoyhandle(&priv->djoylower, priv->djoycbarg); } break; } iinfo("Stored state: %02x\n", priv->djoystate); #else iinfo("Button Ignored. No joystick interface.\n"); #endif /* CONFIG_SPQ10KBD_DJOY */ } else if(state == KEY_PRESS) { /* key is a normal ascii character */ spq10kbd_putbuffer(priv, key); /* Notify waiting reads */ if (priv->waiting == true) { priv->waiting = false; nxsem_post(&priv->waitsem); } } } } /* Clear interrupt status register */ spq10kbd_putreg8(priv, SPQ10KBD_INT, 0); nxsem_post(&priv->exclsem); } /**************************************************************************** * Name: spq10kbd_interrupt ****************************************************************************/ static int spq10kbd_interrupt(int irq, FAR void *context, FAR void *arg) { FAR struct spq10kbd_dev_s *priv = (FAR struct spq10kbd_dev_s *)arg; int ret; /* Let the event worker know that it has an interrupt event to handle * It is possbile that we will already have work scheduled from a * previous interrupt event. That is OK we will service all the events * in the same work job. */ if (work_available(&priv->work)) { ret = work_queue(HPWORK, &priv->work, spq10kbd_worker, priv, 0); if (ret != 0) { ierr("ERROR: Failed to queue work: %d\n", ret); } } return OK; } /**************************************************************************** * Name: spq10kbd_pollnotify ****************************************************************************/ static void spq10kbd_pollnotify(FAR struct spq10kbd_dev_s *priv) { int i; for (i = 0; i < CONFIG_SPQ10KBD_NPOLLWAITERS; i++) { struct pollfd *fds = priv->fds[i]; if (fds) { fds->revents |= (fds->events & POLLIN); if (fds->revents != 0) { uinfo("Report events: %08" PRIx32 "\n", fds->revents); nxsem_post(fds->sem); } } } } /**************************************************************************** * Name: spq10kbd_open * * Description: * Standard character driver open method. * ****************************************************************************/ static int spq10kbd_open(FAR struct file *filep) { FAR struct inode *inode; FAR struct spq10kbd_dev_s *priv; DEBUGASSERT(filep && filep->f_inode); inode = filep->f_inode; priv = inode->i_private; /* Increment the reference count on the driver */ priv->crefs++; return OK; } /**************************************************************************** * Name: spq10kbd_close * * Description: * Standard character driver close method. * ****************************************************************************/ static int spq10kbd_close(FAR struct file *filep) { FAR struct inode *inode; FAR struct spq10kbd_dev_s *priv; DEBUGASSERT(filep && filep->f_inode); inode = filep->f_inode; priv = inode->i_private; /* Decrement the reference count on the driver */ DEBUGASSERT(priv->crefs >= 1); priv->crefs--; return OK; } /**************************************************************************** * Name: spq10kbd_read * * Description: * Standard character driver read method. * ****************************************************************************/ static ssize_t spq10kbd_read(FAR struct file *filep, FAR char *buffer, size_t len) { FAR struct inode *inode; FAR struct spq10kbd_dev_s *priv; size_t nbytes; uint16_t tail; int ret; DEBUGASSERT(filep && filep->f_inode && buffer); inode = filep->f_inode; priv = inode->i_private; /* Read data from our internal buffer of received characters */ ret = nxsem_wait_uninterruptible(&priv->exclsem); if (ret < 0) { return ret; } while (priv->tailndx == priv->headndx) { /* No.. were we open non-blocking? */ if (filep->f_oflags & O_NONBLOCK) { /* Yes.. then return a failure */ ret = -EAGAIN; goto errout; } else { priv->waiting = true; nxsem_post(&priv->exclsem); ret = nxsem_wait_uninterruptible(&priv->waitsem); if (ret < 0) { return ret; } ret = nxsem_wait_uninterruptible(&priv->exclsem); if (ret < 0) { return ret; } } } for (tail = priv->tailndx, nbytes = 0; tail != priv->headndx && nbytes < len; nbytes++) { /* Copy the next keyboard character into the user buffer */ *buffer++ = priv->kbdbuffer[tail]; /* Handle wrap-around of the tail index */ if (++tail >= CONFIG_SPQ10KBD_BUFSIZE) { tail = 0; } } ret = nbytes; /* Update the tail index (perhaps marking the buffer empty) */ priv->tailndx = tail; errout: nxsem_post(&priv->exclsem); return ret; } /**************************************************************************** * Name: spq10kbd_write * * Description: * Standard character driver write method. * ****************************************************************************/ static ssize_t spq10kbd_write(FAR struct file *filep, FAR const char *buffer, size_t len) { /* We won't try to write to the keyboard */ return -ENOSYS; } /**************************************************************************** * Name: spq10kbd_poll * * Description: * Standard character driver poll method. * ****************************************************************************/ static int spq10kbd_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup) { FAR struct inode *inode; FAR struct spq10kbd_dev_s *priv; int ret; int i; DEBUGASSERT(filep && filep->f_inode && fds); inode = filep->f_inode; priv = inode->i_private; /* Make sure that we have exclusive access to the private data structure */ DEBUGASSERT(priv); ret = nxsem_wait_uninterruptible(&priv->exclsem); if (ret < 0) { return ret; } if (setup) { /* This is a request to set up the poll. Find an available slot for * the poll structure reference */ for (i = 0; i < CONFIG_SPQ10KBD_NPOLLWAITERS; i++) { /* Find an available slot */ if (!priv->fds[i]) { /* Bind the poll structure and this slot */ priv->fds[i] = fds; fds->priv = &priv->fds[i]; break; } } if (i >= CONFIG_SPQ10KBD_NPOLLWAITERS) { fds->priv = NULL; ret = -EBUSY; goto errout; } /* Should we immediately notify on any of the requested events? Notify * the POLLIN event if there is buffered keyboard data. */ if (priv->headndx != priv->tailndx) { spq10kbd_pollnotify(priv); } } else { /* This is a request to tear down the poll. */ struct pollfd **slot = (struct pollfd **)fds->priv; DEBUGASSERT(slot); /* Remove all memory of the poll setup */ *slot = NULL; fds->priv = NULL; } errout: nxsem_post(&priv->exclsem); return ret; } /**************************************************************************** * Name: spq10kbd_putbuffer * * Description: * Add one character to the user buffer. * Expectation is that we already have exclusive use of the device. * * Input Parameters: * priv - Driver internal state * keycode - The value to add to the user buffer * * Returned Value: * None * ****************************************************************************/ static void spq10kbd_putbuffer(FAR struct spq10kbd_dev_s *priv, uint8_t keycode) { uint16_t head; uint16_t tail; DEBUGASSERT(priv); /* Copy the next keyboard character into the user buffer. */ head = priv->headndx; priv->kbdbuffer[head] = keycode; /* Increment the head index */ if (++head >= CONFIG_SPQ10KBD_BUFSIZE) { head = 0; } /* If the buffer is full, then increment the tail index to make space. * Drop old unread key presses. */ tail = priv->tailndx; if (tail == head) { if (++tail >= CONFIG_SPQ10KBD_BUFSIZE) { tail = 0; } /* Save the updated tail index */ priv->tailndx = tail; } /* Save the updated head index */ priv->headndx = head; } /**************************************************************************** * Name: spq10kbd_checkver * * Description: * Read and verify the Q10 Keyboard Controller Version * ****************************************************************************/ static int spq10kbd_checkver(FAR struct spq10kbd_dev_s *priv) { uint8_t version; /* Read device version */ version = spq10kbd_getreg8(priv, SPQ10KBD_VER); iinfo("version: %02x\n", version); if (version != SPQ10KBD_VER_00_02) { /* Version is not Correct */ return -ENODEV; } return OK; } /**************************************************************************** * Name: spq10kbd_reset * * Description: * Reset the Q10 Keyboard Controller Version * ****************************************************************************/ static int spq10kbd_reset(FAR struct spq10kbd_dev_s *priv) { spq10kbd_putreg8(priv, SPQ10KBD_RST, 0xff); return OK; } /**************************************************************************** * Name: spq10kbd_getreg8 * * Description: * Read from an 8-bit Q10 Keyboard register * ****************************************************************************/ static uint8_t spq10kbd_getreg8(FAR struct spq10kbd_dev_s *priv, uint8_t regaddr) { /* 8-bit data read sequence: * * Start - I2C_Write_Address - Q10_Reg_Address - * Repeated_Start - I2C_Read_Address - Q10_Read_Data - STOP */ struct i2c_msg_s msg[2]; uint8_t regval; int ret; /* Setup 8-bit Q10 Keyboard address write message */ msg[0].frequency = priv->config->frequency; /* I2C frequency */ msg[0].addr = priv->config->address; /* 7-bit address */ msg[0].flags = 0; /* Write transaction, beginning with START */ msg[0].buffer = ®addr; /* Transfer from this address */ msg[0].length = 1; /* Send one byte following the address * (no STOP) */ /* Set up the 8-bit Q10 Keyboard data read message */ msg[1].frequency = priv->config->frequency; /* I2C frequency */ msg[1].addr = priv->config->address; /* 7-bit address */ msg[1].flags = I2C_M_READ; /* Read transaction, beginning with Re-START */ msg[1].buffer = ®val; /* Transfer to this address */ msg[1].length = 1; /* Receive one byte following the address * (then STOP) */ /* Perform the transfer */ ret = I2C_TRANSFER(priv->i2c, msg, 2); if (ret < 0) { ierr("ERROR: I2C_TRANSFER failed: %d\n", ret); return 0; } #ifdef CONFIG_SPQ10KBD_REGDBG _err("%02x->%02x\n", regaddr, regval); #endif return regval; } /**************************************************************************** * Name: spq10kbd_getreg16 * * Description: * Read from an 8-bit Q10 Keyboard register * ****************************************************************************/ static uint16_t spq10kbd_getreg16(FAR struct spq10kbd_dev_s *priv, uint8_t regaddr) { /* 8-bit data read sequence: * * Start - I2C_Write_Address - Q10_Reg_Address - * Repeated_Start - I2C_Read_Address - Q10_Read_Data - STOP */ struct i2c_msg_s msg[2]; uint8_t regval[2]; uint16_t ret; /* Setup 8-bit Q10 Keyboard address write message */ msg[0].frequency = priv->config->frequency; /* I2C frequency */ msg[0].addr = priv->config->address; /* 7-bit address */ msg[0].flags = 0; /* Write transaction, beginning with START */ msg[0].buffer = ®addr; /* Transfer from this address */ msg[0].length = 1; /* Send one byte following the address * (no STOP) */ /* Set up the 8-bit Q10 Keyboard data read message */ msg[1].frequency = priv->config->frequency; /* I2C frequency */ msg[1].addr = priv->config->address; /* 7-bit address */ msg[1].flags = I2C_M_READ; /* Read transaction, beginning with Re-START */ msg[1].buffer = regval; /* Transfer to this address */ msg[1].length = 2; /* Receive two bytes following the address * (then STOP) */ /* Perform the transfer */ ret = I2C_TRANSFER(priv->i2c, msg, 2); if (ret < 0) { ierr("ERROR: I2C_TRANSFER failed: %d\n", ret); return 0; } ret = (regval[1] << 8) | regval[0]; #ifdef CONFIG_SPQ10KBD_REGDBG _err("%02x->%04x\n", regaddr, ret); #endif return ret; } /**************************************************************************** * Name: spq10kbd_putreg8 * * Description: * Write a value to an 8-bit Q10 Keyboard register * ****************************************************************************/ static void spq10kbd_putreg8(FAR struct spq10kbd_dev_s *priv, uint8_t regaddr, uint8_t regval) { /* 8-bit data read sequence: * * Start - I2C_Write_Address - Q10_Reg_Address - Q10_Write_Data - STOP */ struct i2c_msg_s msg; uint8_t txbuffer[2]; int ret; #ifdef CONFIG_SPQ10KBD_REGDBG _err("%02x<-%02x\n", regaddr, regval); #endif /* Setup to the data to be transferred. Two bytes: The Q10 Keyboard * register address followed by one byte of data. */ txbuffer[0] = regaddr; txbuffer[1] = regval; /* Setup 8-bit Q10 Keyboard address write message */ msg.frequency = priv->config->frequency; /* I2C frequency */ msg.addr = priv->config->address; /* 7-bit address */ msg.flags = 0; /* Write transaction, beginning with START */ msg.buffer = txbuffer; /* Transfer from this address */ msg.length = 2; /* Send two byte following the address * (then STOP) */ /* Perform the transfer */ ret = I2C_TRANSFER(priv->i2c, &msg, 1); if (ret < 0) { ierr("ERROR: I2C_TRANSFER failed: %d\n", ret); } } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: spq10kbd_register * * Description: * Configure the Solder Party Q10 Keyboard to use the provided I2C device * instance. This will register the driver as /dev/kbdN where N is the * minor device number, as well as a joystick at joydevname * * Input Parameters: * i2c - An I2C driver instance * config - Persistent board configuration data * kbdminor - The keyboard input device minor number * joydevname - The name of the joystick device /dev/djoyN * * Returned Value: * Zero is returned on success. Otherwise, a negated errno value is * returned to indicate the nature of the failure. * ****************************************************************************/ #ifdef CONFIG_SPQ10KBD_DJOY int spq10kbd_register(FAR struct i2c_master_s *i2c, FAR const struct spq10kbd_config_s *config, char kbdminor, char *joydevname) #else int spq10kbd_register(FAR struct i2c_master_s *i2c, FAR const struct spq10kbd_config_s *config, char kbdminor) #endif { FAR struct spq10kbd_dev_s *priv; char kbddevname[DEV_NAMELEN]; int ret; /* Debug Sanity Checks */ DEBUGASSERT(i2c != NULL && config != NULL); DEBUGASSERT(config->attach != NULL && config->enable != NULL && config->clear != NULL); priv = (FAR struct spq10kbd_dev_s *)kmm_zalloc( sizeof(struct spq10kbd_dev_s)); if (!priv) { ierr("ERROR: kmm_zalloc(%d) failed\n", sizeof(struct spq10kbd_dev_s)); return -ENOMEM; } /* Initialize the device driver instance */ priv->i2c = i2c; /* Save the I2C device handle */ priv->config = config; /* Save the board configuration */ priv->tailndx = 0; /* Reset keypress buffer state */ priv->headndx = 0; priv->crefs = 0; /* Reset reference count to 0 */ priv->waiting = false; #ifdef CONFIG_SPQ10KBD_DJOY priv->djoylower.config = (FAR void *)priv; priv->djoylower.dl_supported = djoy_supported; priv->djoylower.dl_sample = djoy_sample; priv->djoylower.dl_enable = djoy_enable; priv->djoyhandle = NULL; priv->djoystate = 0; #endif /* CONFIG_SPQ10KBD_DJOY */ nxsem_init(&priv->exclsem, 0, 1); /* Initialize device semaphore */ nxsem_init(&priv->waitsem, 0, 0); /* The waitsem semaphore is used for signaling and, hence, should * not have priority inheritance enabled. */ nxsem_set_protocol(&priv->waitsem, SEM_PRIO_NONE); config->clear(config); config->enable(config, false); /* Attach the interrupt handler */ ret = config->attach(config, spq10kbd_interrupt, priv); if (ret < 0) { ierr("ERROR: Failed to attach interrupt\n"); goto errout_with_priv; } ret = spq10kbd_checkver(priv); if (ret != OK) { /* Did not find a supported device on the bus */ return ret; } spq10kbd_reset(priv); /* Start servicing events */ priv->config->enable(priv->config, true); snprintf(kbddevname, sizeof(kbddevname), DEV_FORMAT, kbdminor); iinfo("Registering %s\n", kbddevname); ret = register_driver(kbddevname, &g_hidkbd_fops, 0666, priv); #ifdef CONFIG_SPQ10KBD_DJOY iinfo("Registering %s\n", joydevname); ret = djoy_register(joydevname, &priv->djoylower); #endif /* CONFIG_SPQ10KBD_DJOY */ return OK; errout_with_priv: nxsem_destroy(&priv->exclsem); kmm_free(priv); return ret; }