2019-11-01 12:50:05 +01:00
# include <linux/device.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/serdev.h>
# include <linux/types.h>
2019-11-01 19:51:37 +01:00
# include <linux/input.h>
2019-11-16 13:22:22 +01:00
# include <linux/gpio/consumer.h>
2019-11-16 21:22:34 +01:00
# include <linux/uaccess.h>
# include <linux/sched.h>
# include <linux/poll.h>
2020-01-03 16:45:28 +01:00
# include <linux/circ_buf.h>
2019-11-01 19:51:37 +01:00
// #define ENABLE_DEBUG ENABLE_DEBUG
2019-11-01 12:50:05 +01:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Markus Koch " ) ;
MODULE_DESCRIPTION ( " LW-35 I/O Controller Driver " ) ;
MODULE_VERSION ( " 0.1 " ) ;
static int debug_level = 0 ;
module_param ( debug_level , int , 0 ) ;
MODULE_PARM_DESC ( debug_level , " Debug level " ) ;
2019-11-16 21:22:34 +01:00
/* Only for chrdev... man they really suck! */
static struct serdev_device * static_serdev = NULL ;
static DECLARE_WAIT_QUEUE_HEAD ( wq ) ;
2020-01-03 16:45:28 +01:00
void pbuf_work_handler ( struct work_struct * work ) ;
DECLARE_WORK ( pbuf_work , pbuf_work_handler ) ;
static DEFINE_SPINLOCK ( printer_status_lock ) ;
2019-11-01 12:50:05 +01:00
static struct of_device_id lw35ioc_dt_ids [ ] = {
{ . compatible = " brother,lw35 " , } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , lw35ioc_dt_ids ) ;
2019-11-16 21:22:34 +01:00
/* Chardev */
# define DEVICE_NAME "lw35"
# define CLASS_NAME "lw35ioc"
2020-01-03 16:45:28 +01:00
/* Only used for initialization, not for actual keycode mapping */
2019-11-01 19:51:37 +01:00
static int keycode_map [ ] = {
KEY_A , KEY_B , KEY_C , KEY_D , KEY_E , KEY_F , KEY_G , KEY_H , KEY_I , KEY_J ,
KEY_K , KEY_L , KEY_M , KEY_N , KEY_O , KEY_P , KEY_Q , KEY_R , KEY_S , KEY_T ,
KEY_U , KEY_V , KEY_W , KEY_X , KEY_Y , KEY_Z ,
KEY_0 , KEY_1 , KEY_2 , KEY_3 , KEY_4 , KEY_5 , KEY_6 , KEY_7 , KEY_8 , KEY_9 ,
KEY_F1 , KEY_F2 , KEY_F3 , KEY_F4 , KEY_F5 , KEY_F6 , KEY_F7 , KEY_F8 , KEY_F9 ,
KEY_F10 , KEY_F11 , KEY_F12 ,
KEY_UP , KEY_DOWN , KEY_RIGHT , KEY_LEFT , KEY_PAGEUP , KEY_PAGEDOWN ,
KEY_102ND , KEY_INSERT , KEY_HOME , KEY_END , KEY_PRINT ,
KEY_CAPSLOCK , KEY_LEFTCTRL , KEY_LEFTSHIFT , KEY_LEFTALT , KEY_TAB ,
KEY_MINUS , KEY_COMMA , KEY_SEMICOLON , KEY_RIGHTBRACE , KEY_LEFTBRACE ,
KEY_GRAVE , KEY_SPACE , KEY_EQUAL , KEY_DOT , KEY_SLASH , KEY_APOSTROPHE ,
KEY_BACKSLASH ,
KEY_DELETE , KEY_ESC , KEY_ENTER , KEY_BACKSPACE ,
KEY_SYSRQ
} ;
2019-11-16 21:22:34 +01:00
enum lw35ioc_state { PASSIVE = 0 , PROG_KBD , PROG_PRI , BYPASS , ACTIVE , STATE_COUNT } ;
2019-11-16 13:22:22 +01:00
enum lw35ioc_packet_state { SYNC , TYPE , KEYBOARD , PRINTER } ;
2019-11-01 19:51:37 +01:00
struct lw35ioc_packet {
2019-11-16 13:22:22 +01:00
enum lw35ioc_packet_state state ;
2020-01-03 16:45:28 +01:00
int payload_offset ;
2019-11-01 19:51:37 +01:00
} ;
2019-11-16 21:22:34 +01:00
struct lw35ioc_chardev {
int major_number ;
struct class * class ;
struct device * device ;
int open_count ;
} ;
2020-01-03 16:45:28 +01:00
struct lw35ioc_printer_status {
int buffer_free ;
int status ;
int reserved0 ;
int reserved1 ;
} ;
2019-11-16 21:22:34 +01:00
# define UART_BUF_SIZE 256
2020-01-03 16:45:28 +01:00
# define PBUF_SIZE 256
2019-11-16 21:22:34 +01:00
2019-11-01 12:50:05 +01:00
struct lw35ioc_priv {
struct serdev_device * serdev ;
2019-11-01 19:51:37 +01:00
struct input_dev * input_dev ;
2019-11-16 13:22:22 +01:00
struct gpio_desc * gpio_rst ; /* GPIO to reset both AVRs */
2019-11-16 21:22:34 +01:00
struct lw35ioc_chardev chardev ;
2020-01-03 16:45:28 +01:00
struct lw35ioc_printer_status printer_status_temp ;
struct lw35ioc_printer_status printer_status ;
2019-11-16 21:22:34 +01:00
char uart_buf_rx [ UART_BUF_SIZE ] ;
char * uart_buf_rx_rd ;
char * uart_buf_rx_wr ;
2020-01-03 16:45:28 +01:00
char pbuf [ PBUF_SIZE ] ;
unsigned long pbuf_tail ;
unsigned long pbuf_head ;
2019-11-16 13:22:22 +01:00
enum lw35ioc_state state ;
2019-11-01 19:51:37 +01:00
struct lw35ioc_packet packet ;
2019-11-01 12:50:05 +01:00
} ;
2019-11-16 13:22:22 +01:00
static int lw35ioc_process_buf ( struct serdev_device * serdev ,
2019-11-01 19:51:37 +01:00
const unsigned char * data ,
size_t count )
2019-11-01 12:50:05 +01:00
{
2019-11-01 19:51:37 +01:00
struct lw35ioc_priv * priv = serdev_device_get_drvdata ( serdev ) ;
unsigned char c ;
int ev ;
int i ;
2020-01-03 16:45:28 +01:00
unsigned long lock_flags ;
2019-11-01 19:51:37 +01:00
# ifdef ENABLE_DEBUG
dev_info ( & serdev - > dev , " IRQ. \n " ) ;
# endif
for ( i = 0 ; i < count ; + + i ) {
c = data [ i ] ;
# ifdef ENABLE_DEBUG
dev_info ( & serdev - > dev , " RX [%d]: %02x \n " , priv - > packet . state , c ) ;
# endif
switch ( priv - > packet . state ) {
case SYNC :
if ( c = = 0xFF )
priv - > packet . state = TYPE ;
break ;
2020-01-03 16:45:28 +01:00
2019-11-01 19:51:37 +01:00
case TYPE :
if ( c = = 0x55 )
priv - > packet . state = KEYBOARD ;
else if ( c = = 0xAA )
priv - > packet . state = PRINTER ;
else
priv - > packet . state = SYNC ;
2020-01-03 16:45:28 +01:00
priv - > packet . payload_offset = 0 ;
2019-11-01 19:51:37 +01:00
break ;
2020-01-03 16:45:28 +01:00
2019-11-01 19:51:37 +01:00
case KEYBOARD :
if ( c > 127 ) {
c - = 128 ;
ev = 1 ;
} else {
ev = 0 ;
}
input_report_key ( priv - > input_dev , c , ev ) ;
input_sync ( priv - > input_dev ) ;
priv - > packet . state = SYNC ;
break ;
2020-01-03 16:45:28 +01:00
2019-11-01 19:51:37 +01:00
case PRINTER :
2020-01-03 16:45:28 +01:00
switch ( priv - > packet . payload_offset ) {
case 0 : /* Status */
priv - > printer_status_temp . status = ( int ) c ;
break ;
case 1 : /* Buffer status */
priv - > printer_status_temp . buffer_free = ( int ) c ;
break ;
case 2 : /* Reserved */
priv - > printer_status_temp . reserved0 = ( int ) c ;
break ;
case 3 : /* Reserved */
priv - > printer_status_temp . reserved1 = ( int ) c ;
dev_info ( & serdev - > dev ,
" New printer status. status=0x%02X buffer_free=0x%02X [0x%02X,0x%02X]. \n " ,
priv - > printer_status_temp . status ,
priv - > printer_status_temp . buffer_free ,
priv - > printer_status_temp . reserved0 ,
priv - > printer_status_temp . reserved1 ) ;
if ( priv - > printer_status_temp . status = = 0xFF ) {
dev_err ( & serdev - > dev ,
" HARDFAULT in printer firmware! Reset required. \n " ) ;
}
spin_lock_irqsave ( & printer_status_lock , lock_flags ) ;
memcpy ( & priv - > printer_status ,
& priv - > printer_status_temp ,
sizeof ( priv - > printer_status ) ) ;
spin_unlock_irqrestore ( & printer_status_lock , lock_flags ) ;
if ( priv - > printer_status_temp . buffer_free > 0 )
schedule_work ( & pbuf_work ) ;
priv - > packet . state = SYNC ;
break ;
default :
break ;
}
priv - > packet . payload_offset + + ;
2019-11-01 19:51:37 +01:00
break ;
}
}
return i ;
2019-11-01 12:50:05 +01:00
}
2019-11-16 13:22:22 +01:00
static int lw35ioc_receive_buf ( struct serdev_device * serdev ,
const unsigned char * data ,
size_t count )
{
struct lw35ioc_priv * priv = serdev_device_get_drvdata ( serdev ) ;
2019-11-16 21:22:34 +01:00
int i ;
2020-01-03 16:45:28 +01:00
switch ( priv - > state ) {
case ACTIVE :
2019-11-16 13:22:22 +01:00
return lw35ioc_process_buf ( serdev , data , count ) ;
2020-01-03 16:45:28 +01:00
break ;
case PASSIVE :
break ;
default :
/* Pass data directly to chardev */
for ( i = 0 ; i < count ; + + i ) {
2019-11-16 21:22:34 +01:00
# ifdef ENABLE_DEBUG
2020-01-03 16:45:28 +01:00
dev_info ( & serdev - > dev , " RX [PSV]: %02x \n " , data [ i ] ) ;
2019-11-16 21:22:34 +01:00
# endif
2020-01-03 16:45:28 +01:00
* ( priv - > uart_buf_rx_wr ) = data [ i ] ;
if ( priv - > uart_buf_rx_wr = = priv - > uart_buf_rx + UART_BUF_SIZE - 1 )
priv - > uart_buf_rx_wr = priv - > uart_buf_rx ;
else
priv - > uart_buf_rx_wr + + ;
if ( priv - > uart_buf_rx_rd = = priv - > uart_buf_rx_wr ) {
dev_err ( & serdev - > dev , " UART RX buffer overflow. \n " ) ;
}
2019-11-16 21:22:34 +01:00
}
2020-01-03 16:45:28 +01:00
wake_up_interruptible ( & wq ) ;
}
2019-11-16 21:22:34 +01:00
2019-11-16 13:22:22 +01:00
return count ; /* Pretend we read all data */
}
2019-11-01 12:50:05 +01:00
static void lw35ioc_write_wakeup ( struct serdev_device * serdev )
{
// schedule work here
}
static struct serdev_device_ops lw35ioc_serdev_ops = {
. receive_buf = lw35ioc_receive_buf ,
. write_wakeup = lw35ioc_write_wakeup ,
} ;
2019-11-16 13:22:22 +01:00
static void lw35ioc_set_hw_reset ( struct serdev_device * serdev ,
int reset_active )
{
struct lw35ioc_priv * priv = serdev_device_get_drvdata ( serdev ) ;
2019-11-16 21:22:34 +01:00
gpiod_set_value ( priv - > gpio_rst , ! ! reset_active ) ;
}
static void lw35ioc_reset_ioc_hw ( struct serdev_device * serdev )
{
dev_info ( & serdev - > dev , " Resetting IOC... \n " ) ;
lw35ioc_set_hw_reset ( serdev , 1 ) ;
usleep_range ( 90000 , 100000 ) ;
lw35ioc_set_hw_reset ( serdev , 0 ) ;
usleep_range ( 90000 , 100000 ) ;
}
/* Chardev */
static int lw35ioc_chardev_open ( struct inode * inodep , struct file * filep ) {
struct serdev_device * serdev = static_serdev ;
struct lw35ioc_priv * priv = serdev_device_get_drvdata ( serdev ) ;
filep - > private_data = serdev ;
if ( ! priv - > chardev . open_count ) {
dev_info ( & serdev - > dev , " Opened chardev. \n " ) ;
2020-01-03 16:45:28 +01:00
switch ( priv - > state ) {
case PROG_KBD :
2019-11-16 21:22:34 +01:00
lw35ioc_reset_ioc_hw ( serdev ) ;
2020-01-03 16:45:28 +01:00
serdev_device_write ( serdev , " X " , 1 , 0 ) ;
dev_info ( & serdev - > dev , " Put ioc into programming mode: keyboard. \n " ) ;
break ;
case PROG_PRI :
lw35ioc_reset_ioc_hw ( serdev ) ;
serdev_device_write ( serdev , " Y " , 1 , 0 ) ;
dev_info ( & serdev - > dev , " Put ioc into programming mode: printer. \n " ) ;
break ;
case BYPASS :
lw35ioc_reset_ioc_hw ( serdev ) ;
serdev_device_write ( serdev , " XX " , 2 , 0 ) ;
dev_info ( & serdev - > dev , " Put ioc into bypass mode. \n " ) ;
break ;
case PASSIVE :
lw35ioc_reset_ioc_hw ( serdev ) ;
usleep_range ( 1000000 , 1010000 ) ;
dev_info ( & serdev - > dev , " Put ioc into active mode. \n " ) ;
priv - > state = ACTIVE ;
break ;
case ACTIVE :
/* Don't do anything when active */
break ;
default :
break ;
2019-11-16 21:22:34 +01:00
}
2020-01-03 16:45:28 +01:00
2019-11-16 21:22:34 +01:00
} else {
dev_err ( & serdev - > dev , " Rejected. Device may only be opened once. \n " ) ;
2020-01-03 16:45:28 +01:00
//return -EBUSY; // TODO
2019-11-16 21:22:34 +01:00
}
priv - > chardev . open_count + + ;
return 0 ;
}
static ssize_t lw35ioc_chardev_read ( struct file * filep , char * buffer , size_t len , loff_t * offset ) {
struct serdev_device * serdev = filep - > private_data ;
struct lw35ioc_priv * priv = serdev_device_get_drvdata ( serdev ) ;
int error_count ;
int available ;
available = priv - > uart_buf_rx_wr - priv - > uart_buf_rx_rd ;
if ( available < 0 )
available = 1 ;
if ( ! available ) { /* Wait for data to become available */
wait_event_interruptible ( wq , priv - > uart_buf_rx_rd ! = priv - > uart_buf_rx_wr ) ;
available = priv - > uart_buf_rx_wr - priv - > uart_buf_rx_rd ;
if ( available < 0 )
2020-01-03 16:45:28 +01:00
available = 1 ; /* TODO: We should calculate the remaining block until wraparound here... */
2019-11-16 21:22:34 +01:00
if ( ! available )
return - EAGAIN ;
}
if ( available < len )
len = available ;
error_count = copy_to_user ( buffer , priv - > uart_buf_rx_rd , len ) ;
len - = error_count ;
priv - > uart_buf_rx_rd + = len ;
if ( priv - > uart_buf_rx_rd > = priv - > uart_buf_rx + UART_BUF_SIZE ) {
priv - > uart_buf_rx_rd - = UART_BUF_SIZE ;
}
if ( error_count = = 0 ) {
return len ;
} else {
dev_err ( & serdev - > dev , " Failed to read %d characters to chardev. \n " ,
error_count ) ;
return - EFAULT ;
}
}
char msg [ 256 ] ; // TODO
static ssize_t lw35ioc_chardev_write ( struct file * filep , const char * buffer , size_t len , loff_t * offset ) {
struct serdev_device * serdev = filep - > private_data ;
struct lw35ioc_priv * priv = serdev_device_get_drvdata ( serdev ) ;
2020-01-03 16:45:28 +01:00
unsigned long tail ;
int i ;
int ret ;
switch ( priv - > state ) {
case ACTIVE :
/* Put data into pbuf */
// TODO: spinlock
if ( CIRC_SPACE ( priv - > pbuf_head , READ_ONCE ( priv - > pbuf_tail ) , PBUF_SIZE ) < 1 ) {
dev_err ( & serdev - > dev , " chdev write: Waiting for space... \n " ) ;
if ( ret = ( wait_event_interruptible ( wq , CIRC_SPACE ( priv - > pbuf_head , priv - > pbuf_tail , PBUF_SIZE ) > = 1 ) ) ) { // TODO: Accessing pbuf_tail without any protection... this might be bad?
dev_err ( & serdev - > dev ,
" chdev write: device closed while waiting. ret: %d \n " ,
ret ) ;
// TODO: This is not proper handling of this I guess...
return len ;
}
dev_err ( & serdev - > dev , " chdev write: done waiting... \n " ) ;
}
2019-11-16 21:22:34 +01:00
2020-01-03 16:45:28 +01:00
tail = READ_ONCE ( priv - > pbuf_tail ) ;
for ( i = 0 ; i < len ; + + i ) { // TODO: Block copy would be faster...
if ( CIRC_SPACE ( priv - > pbuf_head , tail , PBUF_SIZE ) > = 1 ) {
ret = copy_from_user ( priv - > pbuf + priv - > pbuf_head ,
buffer + i ,
1 ) ;
if ( ret ) {
dev_err ( & serdev - > dev , " chdev write: copy_from_user failed (%d). \n " ,
ret ) ;
i = len - 1 ;
}
smp_store_release ( & ( priv - > pbuf_head ) ,
( priv - > pbuf_head + 1 ) & ( PBUF_SIZE - 1 ) ) ;
} else {
dev_warn ( & serdev - > dev , " chdev write: ring buffer full. \n " ) ;
break ;
}
}
len = i ;
schedule_work ( & pbuf_work ) ;
dev_info ( & serdev - > dev ,
" Copied %d bytes to FIFO (FIFO free: %d) " ,
len , CIRC_SPACE ( priv - > pbuf_head , tail , PBUF_SIZE ) ) ;
break ;
case PASSIVE :
len = - EBUSY ; // TODO: Can I do this?
break ;
default : /* Pass-through */
if ( len > 256 ) {
len = 256 ;
}
copy_from_user ( msg , buffer , len ) ; // TODO: Return value
serdev_device_write_buf ( serdev , msg , len ) ;
2019-11-16 21:22:34 +01:00
}
return len ;
}
static int lw35ioc_chardev_release ( struct inode * inodep , struct file * filep ) {
struct serdev_device * serdev = filep - > private_data ;
struct lw35ioc_priv * priv = serdev_device_get_drvdata ( serdev ) ;
priv - > chardev . open_count - - ;
dev_info ( & serdev - > dev , " Device successfully closed \n " ) ;
return 0 ;
}
# define IOCTL_SET_MODE 42
static long lw35ioc_chardev_ioctl ( struct file * filep , unsigned int cmd ,
unsigned long arg )
{
struct serdev_device * serdev = filep - > private_data ;
struct lw35ioc_priv * priv = serdev_device_get_drvdata ( serdev ) ;
switch ( cmd ) {
case IOCTL_SET_MODE :
if ( arg > = STATE_COUNT | | arg < 0 ) {
dev_err ( & serdev - > dev , " Invalid driver mode: %lu " , arg ) ;
return - 1 ;
}
priv - > state = arg ;
dev_info ( & serdev - > dev , " Switched driver mode to: %lu. Will be applied with next open(). \n " , arg ) ;
break ;
default :
dev_info ( & serdev - > dev , " Ignoring IOCTL %u(%lu) \n " , cmd , arg ) ;
break ;
}
return 0 ;
}
__poll_t poll ( struct file * filep , struct poll_table_struct * ptab )
{
struct serdev_device * serdev = filep - > private_data ;
struct lw35ioc_priv * priv = serdev_device_get_drvdata ( serdev ) ;
poll_wait ( filep , & wq , ptab ) ;
if ( priv - > uart_buf_rx_wr ! = priv - > uart_buf_rx_rd )
return EPOLLIN | EPOLLRDNORM ;
return 0 ;
}
2020-01-03 16:45:28 +01:00
# define PRINTER_READY_MASK (0b00011111)
int printer_ready_for_data ( struct lw35ioc_printer_status printer_status )
{
return ( ( printer_status . status & PRINTER_READY_MASK ) = = PRINTER_READY_MASK ) ;
}
void pbuf_work_handler ( struct work_struct * work )
{
// TODO: get private data using container_of(): https://linux-kernel-labs.github.io/master/labs/deferred_work.html
struct serdev_device * serdev = static_serdev ; // Not cool, but whatever...
struct lw35ioc_priv * priv = serdev_device_get_drvdata ( serdev ) ;
unsigned long head ;
unsigned long tail ;
unsigned long lock_flags ;
int bsize = 0 ;
int i ;
int locked = 0 ;
dev_info ( & serdev - > dev ,
" pbuf_work_handler \n " ) ;
while ( 1 ) {
// TODO: We are the only consumer, so we should be fine without a spinlock, right?
head = smp_load_acquire ( & priv - > pbuf_head ) ;
tail = priv - > pbuf_tail ;
bsize = CIRC_CNT ( head , tail , PBUF_SIZE ) ;
dev_info ( & serdev - > dev , " CIRC CNT=%d \n " , bsize ) ;
if ( bsize = = 0 )
break ;
spin_lock_irqsave ( & printer_status_lock , lock_flags ) ;
locked = 1 ;
if ( ! printer_ready_for_data ( priv - > printer_status ) ) {
dev_warn ( & serdev - > dev , " Cannot transmit data to device. Device is not ready. \n " ) ;
break ;
}
if ( bsize > priv - > printer_status . buffer_free )
bsize = priv - > printer_status . buffer_free ;
if ( bsize > CIRC_CNT_TO_END ( head , tail , PBUF_SIZE ) )
bsize = CIRC_CNT_TO_END ( head , tail , PBUF_SIZE ) ;
if ( bsize > priv - > printer_status . buffer_free - 20 )
bsize = priv - > printer_status . buffer_free - 20 ; // TODO: Bug workaround
dev_info ( & serdev - > dev , " TX %d of %d bytes [%d bytes free on device]. \n " ,
bsize ,
CIRC_CNT ( head , tail , PBUF_SIZE ) ,
priv - > printer_status . buffer_free ) ;
priv - > printer_status . buffer_free - = bsize ;
spin_unlock_irqrestore ( & printer_status_lock , lock_flags ) ;
locked = 0 ;
if ( bsize = = 0 ) {
dev_warn ( & serdev - > dev , " Cannot transmit data to device. Device buffer is full. \n " ) ;
break ;
}
serdev_device_write_buf ( serdev ,
priv - > pbuf + tail ,
bsize ) ;
for ( i = 0 ; i < bsize ; + + i ) {
dev_info ( & serdev - > dev ,
" UART TX: %c \n " ,
priv - > pbuf [ tail + i ] ) ;
}
smp_store_release ( & priv - > pbuf_tail ,
( tail + bsize ) & ( PBUF_SIZE - 1 ) ) ;
}
if ( locked )
spin_unlock_irqrestore ( & printer_status_lock , lock_flags ) ;
wake_up_interruptible ( & wq ) ; // There's some space available in the FIFO now, so wake up chardev write
}
2019-11-16 21:22:34 +01:00
static struct file_operations fops =
{
. open = lw35ioc_chardev_open ,
. read = lw35ioc_chardev_read ,
. write = lw35ioc_chardev_write ,
. release = lw35ioc_chardev_release ,
. unlocked_ioctl = lw35ioc_chardev_ioctl ,
. poll = poll ,
} ;
static int lw35ioc_cdev_init ( struct serdev_device * serdev )
{
struct lw35ioc_priv * priv = serdev_device_get_drvdata ( serdev ) ;
priv - > chardev . open_count = 0 ;
// Try to dynamically allocate a major number for the device
priv - > chardev . major_number = register_chrdev ( 0 , DEVICE_NAME , & fops ) ;
if ( priv - > chardev . major_number < 0 ) {
dev_err ( & serdev - > dev , " Failed to register a major number \n " ) ;
return priv - > chardev . major_number ;
}
dev_info ( & serdev - > dev , " Registered correctly with major number %d \n " , priv - > chardev . major_number ) ;
// Register the device class
priv - > chardev . class = class_create ( THIS_MODULE , CLASS_NAME ) ;
if ( IS_ERR ( priv - > chardev . class ) ) {
unregister_chrdev ( priv - > chardev . major_number , DEVICE_NAME ) ;
dev_err ( & serdev - > dev , " Failed to register device class \n " ) ;
return PTR_ERR ( priv - > chardev . class ) ;
}
dev_info ( & serdev - > dev , " Device class registered correctly \n " ) ;
// Register the device driver
priv - > chardev . device = device_create ( priv - > chardev . class , NULL , MKDEV ( priv - > chardev . major_number , 0 ) , NULL , DEVICE_NAME ) ;
if ( IS_ERR ( priv - > chardev . device ) ) {
class_destroy ( priv - > chardev . class ) ;
unregister_chrdev ( priv - > chardev . major_number , DEVICE_NAME ) ;
dev_err ( & serdev - > dev , " Failed to create the device \n " ) ;
return PTR_ERR ( priv - > chardev . device ) ;
}
2020-01-03 16:45:28 +01:00
dev_info ( & serdev - > dev , " Device class created correctly \n " ) ;
2019-11-16 21:22:34 +01:00
return 0 ;
2019-11-16 13:22:22 +01:00
}
2019-11-01 12:50:05 +01:00
static int lw35ioc_probe ( struct serdev_device * serdev )
{
struct lw35ioc_priv * priv = NULL ;
2019-11-16 21:22:34 +01:00
int ret = 0 ;
2019-11-01 19:51:37 +01:00
int i ;
2019-11-10 19:49:32 +01:00
u32 speed = 19200 ;
2019-11-01 19:51:37 +01:00
struct input_dev * input_dev ;
2019-11-01 12:50:05 +01:00
printk ( KERN_INFO " lw35ioc: probe \n " ) ;
2019-11-16 21:22:34 +01:00
if ( static_serdev ) {
printk ( KERN_ERR " lw35ioc: Can only be loaded once. \n " ) ;
goto fail_multicheck ;
}
2019-11-16 13:22:22 +01:00
/* Allocate memory */
2019-11-01 19:51:37 +01:00
priv = devm_kzalloc ( & serdev - > dev ,
sizeof ( struct lw35ioc_priv ) ,
GFP_KERNEL ) ;
if ( ! priv ) {
printk ( KERN_ERR " lw35ioc: Not enough memory. \n " ) ;
return - ENOMEM ;
}
2019-11-16 21:22:34 +01:00
2019-11-16 13:22:22 +01:00
priv - > state = PASSIVE ;
2019-11-16 21:22:34 +01:00
priv - > uart_buf_rx_rd = priv - > uart_buf_rx ;
priv - > uart_buf_rx_wr = priv - > uart_buf_rx ;
2020-01-03 16:45:28 +01:00
priv - > pbuf_tail = 0 ;
priv - > pbuf_head = 0 ;
priv - > printer_status . buffer_free = 0 ;
priv - > printer_status . status = 0 ;
2019-11-01 12:50:05 +01:00
priv - > serdev = serdev ;
2019-11-16 21:22:34 +01:00
static_serdev = serdev ;
2019-11-01 19:51:37 +01:00
priv - > packet . state = SYNC ;
2019-11-01 12:50:05 +01:00
serdev_device_set_drvdata ( serdev , priv ) ;
2019-11-16 13:22:22 +01:00
/* Register input device */
2019-11-01 19:51:37 +01:00
input_dev = devm_input_allocate_device ( & serdev - > dev ) ;
if ( ! input_dev ) {
dev_err ( & serdev - > dev , " Unable to allocate input device. \n " ) ;
return - ENOMEM ;
}
priv - > input_dev = input_dev ;
input_dev - > name = " lw35ioc " ;
input_dev - > phys = " lw35ioc/input1 " ;
input_set_capability ( input_dev , EV_MSC , MSC_SCAN ) ;
__set_bit ( EV_KEY , input_dev - > evbit ) ;
__set_bit ( EV_REP , input_dev - > evbit ) ;
for ( i = 0 ; i < ARRAY_SIZE ( keycode_map ) ; i + + )
__set_bit ( keycode_map [ i ] , input_dev - > keybit ) ;
ret = input_register_device ( input_dev ) ;
if ( ret ) {
dev_err ( & serdev - > dev , " Unable to register input device. \n " ) ;
2019-11-16 13:22:22 +01:00
goto fail_input ;
2019-11-01 19:51:37 +01:00
}
2020-01-03 16:45:28 +01:00
serdev_device_set_client_ops ( serdev , & lw35ioc_serdev_ops ) ;
2019-11-01 12:50:05 +01:00
ret = serdev_device_open ( serdev ) ;
if ( ret ) {
dev_err ( & serdev - > dev , " Unable to open device. \n " ) ;
2019-11-16 13:22:22 +01:00
goto fail_serdev ;
2019-11-01 12:50:05 +01:00
}
speed = serdev_device_set_baudrate ( serdev , speed ) ;
dev_info ( & serdev - > dev , " Using baudrate: %u \n " , speed ) ;
2019-11-16 13:22:22 +01:00
/* Register GPIO */
priv - > gpio_rst = gpiod_get_index ( & serdev - > dev ,
" rst " ,
0 ,
2019-11-16 21:22:34 +01:00
GPIOD_OUT_HIGH ) ;
/* Register character device */
ret = lw35ioc_cdev_init ( serdev ) ;
if ( ret ) {
dev_err ( & serdev - > dev , " Unable to initialize character device. \n " ) ;
goto fail_gpio ;
}
2019-11-16 13:22:22 +01:00
if ( IS_ERR ( priv - > gpio_rst ) ) {
dev_err ( & serdev - > dev , " Unable to get reset GPIO. \n " ) ;
2019-11-16 21:22:34 +01:00
ret = - ENODEV ; /* TODO */
2019-11-16 13:22:22 +01:00
goto fail_gpio ;
}
2019-11-16 21:22:34 +01:00
lw35ioc_reset_ioc_hw ( serdev ) ;
2019-11-01 19:51:37 +01:00
dev_info ( & serdev - > dev , " Loaded driver. \n " ) ;
2019-11-01 12:50:05 +01:00
return 0 ;
2019-11-16 13:22:22 +01:00
fail_gpio :
fail_serdev :
2019-11-01 19:51:37 +01:00
input_unregister_device ( input_dev ) ;
2019-11-16 13:22:22 +01:00
fail_input :
2019-11-01 19:51:37 +01:00
devm_kfree ( & serdev - > dev , input_dev ) ;
2019-11-16 21:22:34 +01:00
fail_multicheck :
2019-11-01 12:50:05 +01:00
return ret ;
}
static void lw35ioc_remove ( struct serdev_device * serdev )
{
struct lw35ioc_priv * priv = serdev_device_get_drvdata ( serdev ) ;
2019-11-01 19:51:37 +01:00
2020-01-03 16:45:28 +01:00
dev_info ( & serdev - > dev , " remove \n " ) ;
dev_info ( & serdev - > dev , " cdev class: %p \n " , priv - > chardev . class ) ;
2019-11-16 21:22:34 +01:00
device_destroy ( priv - > chardev . class , MKDEV ( priv - > chardev . major_number , 0 ) ) ;
2020-01-03 16:45:28 +01:00
dev_info ( & serdev - > dev , " unreg \n " ) ;
2019-11-16 21:22:34 +01:00
class_unregister ( priv - > chardev . class ) ;
2020-01-03 16:45:28 +01:00
dev_info ( & serdev - > dev , " destroy: SKIP \n " ) ;
//class_destroy(priv->chardev.class);
dev_info ( & serdev - > dev , " unreg-dev \n " ) ;
2019-11-16 21:22:34 +01:00
unregister_chrdev ( priv - > chardev . major_number , DEVICE_NAME ) ;
2020-01-03 16:45:28 +01:00
dev_info ( & serdev - > dev , " gpio rst: %p \n " , priv - > gpio_rst ) ;
dev_info ( & serdev - > dev , " serdev: %p \n " , priv - > serdev ) ;
dev_info ( & serdev - > dev , " input dev: %p \n " , priv - > input_dev ) ;
2019-11-16 13:22:22 +01:00
lw35ioc_set_hw_reset ( serdev , 1 ) ;
gpiod_put ( priv - > gpio_rst ) ;
2019-11-01 19:51:37 +01:00
serdev_device_close ( priv - > serdev ) ;
input_unregister_device ( priv - > input_dev ) ;
2020-01-03 16:45:28 +01:00
dev_info ( & serdev - > dev , " done. \n " ) ;
2019-11-01 12:50:05 +01:00
return ;
}
static struct serdev_device_driver lw35ioc_driver = {
. probe = lw35ioc_probe ,
. remove = lw35ioc_remove ,
. driver = {
. name = " lw35ioc " ,
. owner = THIS_MODULE ,
. of_match_table = of_match_ptr ( lw35ioc_dt_ids ) ,
} ,
} ;
module_serdev_device_driver ( lw35ioc_driver ) ;