2006-06-22 19:04:47 +02:00
/*
* cypress_cy7c63 . c
*
2007-11-12 00:42:16 +01:00
* Copyright ( c ) 2006 - 2007 Oliver Bock ( bock @ tfh - berlin . de )
2006-06-22 19:04:47 +02:00
*
* This driver is based on the Cypress USB Driver by Marcus Maul
* ( cyport ) and the 2.0 version of Greg Kroah - Hartman ' s
* USB Skeleton driver .
*
* This is a generic driver for the Cypress CY7C63xxx family .
* For the time being it enables you to read from and write to
* the single I / O ports of the device .
*
* Supported vendors : AK Modul - Bus Computer GmbH
2006-07-27 21:34:58 +02:00
* ( Firmware " Port-Chip " )
*
* Supported devices : CY7C63001A - PC
* CY7C63001C - PXC
* CY7C63001C - SXC
*
* Supported functions : Read / Write Ports
2006-06-22 19:04:47 +02:00
*
*
2007-11-12 00:42:16 +01:00
* For up - to - date information please visit :
* http : //www.obock.de/kernel/cypress
*
2006-06-22 19:04:47 +02:00
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation , version 2.
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/usb.h>
2007-11-12 00:42:16 +01:00
# define DRIVER_AUTHOR "Oliver Bock (bock@tfh-berlin.de)"
2006-06-22 19:04:47 +02:00
# define DRIVER_DESC "Cypress CY7C63xxx USB driver"
# define CYPRESS_VENDOR_ID 0xa2c
# define CYPRESS_PRODUCT_ID 0x8
# define CYPRESS_READ_PORT 0x4
# define CYPRESS_WRITE_PORT 0x5
# define CYPRESS_READ_RAM 0x2
# define CYPRESS_WRITE_RAM 0x3
# define CYPRESS_READ_ROM 0x1
# define CYPRESS_READ_PORT_ID0 0
# define CYPRESS_WRITE_PORT_ID0 0
# define CYPRESS_READ_PORT_ID1 0x2
# define CYPRESS_WRITE_PORT_ID1 1
# define CYPRESS_MAX_REQSIZE 8
/* table of devices that work with this driver */
static struct usb_device_id cypress_table [ ] = {
{ USB_DEVICE ( CYPRESS_VENDOR_ID , CYPRESS_PRODUCT_ID ) } ,
{ }
} ;
MODULE_DEVICE_TABLE ( usb , cypress_table ) ;
/* structure to hold all of our device specific stuff */
struct cypress {
struct usb_device * udev ;
unsigned char port [ 2 ] ;
} ;
/* used to send usb control messages to device */
static int vendor_command ( struct cypress * dev , unsigned char request ,
unsigned char address , unsigned char data )
{
int retval = 0 ;
unsigned int pipe ;
unsigned char * iobuf ;
/* allocate some memory for the i/o buffer*/
iobuf = kzalloc ( CYPRESS_MAX_REQSIZE , GFP_KERNEL ) ;
if ( ! iobuf ) {
dev_err ( & dev - > udev - > dev , " Out of memory! \n " ) ;
retval = - ENOMEM ;
goto error ;
}
dev_dbg ( & dev - > udev - > dev , " Sending usb_control_msg (data: %d) \n " , data ) ;
/* prepare usb control message and send it upstream */
pipe = usb_rcvctrlpipe ( dev - > udev , 0 ) ;
retval = usb_control_msg ( dev - > udev , pipe , request ,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER ,
address , data , iobuf , CYPRESS_MAX_REQSIZE ,
USB_CTRL_GET_TIMEOUT ) ;
/* store returned data (more READs to be added) */
switch ( request ) {
case CYPRESS_READ_PORT :
if ( address = = CYPRESS_READ_PORT_ID0 ) {
dev - > port [ 0 ] = iobuf [ 1 ] ;
dev_dbg ( & dev - > udev - > dev ,
" READ_PORT0 returned: %d \n " ,
dev - > port [ 0 ] ) ;
}
else if ( address = = CYPRESS_READ_PORT_ID1 ) {
dev - > port [ 1 ] = iobuf [ 1 ] ;
dev_dbg ( & dev - > udev - > dev ,
" READ_PORT1 returned: %d \n " ,
dev - > port [ 1 ] ) ;
}
break ;
}
kfree ( iobuf ) ;
error :
return retval ;
}
/* write port value */
static ssize_t write_port ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count ,
int port_num , int write_id )
{
int value = - 1 ;
int result = 0 ;
struct usb_interface * intf = to_usb_interface ( dev ) ;
struct cypress * cyp = usb_get_intfdata ( intf ) ;
dev_dbg ( & cyp - > udev - > dev , " WRITE_PORT%d called \n " , port_num ) ;
/* validate input data */
if ( sscanf ( buf , " %d " , & value ) < 1 ) {
result = - EINVAL ;
goto error ;
}
if ( value < 0 | | value > 255 ) {
result = - EINVAL ;
goto error ;
}
result = vendor_command ( cyp , CYPRESS_WRITE_PORT , write_id ,
( unsigned char ) value ) ;
dev_dbg ( & cyp - > udev - > dev , " Result of vendor_command: %d \n \n " , result ) ;
error :
return result < 0 ? result : count ;
}
/* attribute callback handler (write) */
static ssize_t set_port0_handler ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
return write_port ( dev , attr , buf , count , 0 , CYPRESS_WRITE_PORT_ID0 ) ;
}
/* attribute callback handler (write) */
static ssize_t set_port1_handler ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
return write_port ( dev , attr , buf , count , 1 , CYPRESS_WRITE_PORT_ID1 ) ;
}
/* read port value */
static ssize_t read_port ( struct device * dev , struct device_attribute * attr ,
char * buf , int port_num , int read_id )
{
int result = 0 ;
struct usb_interface * intf = to_usb_interface ( dev ) ;
struct cypress * cyp = usb_get_intfdata ( intf ) ;
dev_dbg ( & cyp - > udev - > dev , " READ_PORT%d called \n " , port_num ) ;
result = vendor_command ( cyp , CYPRESS_READ_PORT , read_id , 0 ) ;
dev_dbg ( & cyp - > udev - > dev , " Result of vendor_command: %d \n \n " , result ) ;
return sprintf ( buf , " %d " , cyp - > port [ port_num ] ) ;
}
/* attribute callback handler (read) */
static ssize_t get_port0_handler ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
return read_port ( dev , attr , buf , 0 , CYPRESS_READ_PORT_ID0 ) ;
}
/* attribute callback handler (read) */
static ssize_t get_port1_handler ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
return read_port ( dev , attr , buf , 1 , CYPRESS_READ_PORT_ID1 ) ;
}
static DEVICE_ATTR ( port0 , S_IWUGO | S_IRUGO ,
get_port0_handler , set_port0_handler ) ;
static DEVICE_ATTR ( port1 , S_IWUGO | S_IRUGO ,
get_port1_handler , set_port1_handler ) ;
static int cypress_probe ( struct usb_interface * interface ,
const struct usb_device_id * id )
{
struct cypress * dev = NULL ;
int retval = - ENOMEM ;
/* allocate memory for our device state and initialize it */
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
if ( dev = = NULL ) {
2006-08-14 17:32:51 -07:00
dev_err ( & interface - > dev , " Out of memory! \n " ) ;
2006-08-28 11:43:25 -07:00
goto error_mem ;
2006-06-22 19:04:47 +02:00
}
dev - > udev = usb_get_dev ( interface_to_usbdev ( interface ) ) ;
/* save our data pointer in this interface device */
usb_set_intfdata ( interface , dev ) ;
/* create device attribute files */
2006-08-28 11:43:25 -07:00
retval = device_create_file ( & interface - > dev , & dev_attr_port0 ) ;
if ( retval )
goto error ;
retval = device_create_file ( & interface - > dev , & dev_attr_port1 ) ;
if ( retval )
goto error ;
2006-06-22 19:04:47 +02:00
/* let the user know that the device is now attached */
dev_info ( & interface - > dev ,
" Cypress CY7C63xxx device now attached \n " ) ;
2006-08-28 11:43:25 -07:00
return 0 ;
2006-06-22 19:04:47 +02:00
error :
2006-08-28 11:43:25 -07:00
device_remove_file ( & interface - > dev , & dev_attr_port0 ) ;
device_remove_file ( & interface - > dev , & dev_attr_port1 ) ;
usb_set_intfdata ( interface , NULL ) ;
usb_put_dev ( dev - > udev ) ;
kfree ( dev ) ;
error_mem :
2006-06-22 19:04:47 +02:00
return retval ;
}
static void cypress_disconnect ( struct usb_interface * interface )
{
struct cypress * dev ;
dev = usb_get_intfdata ( interface ) ;
/* remove device attribute files */
device_remove_file ( & interface - > dev , & dev_attr_port0 ) ;
device_remove_file ( & interface - > dev , & dev_attr_port1 ) ;
2007-03-30 10:52:16 +02:00
/* the intfdata can be set to NULL only after the
* device files have been removed */
usb_set_intfdata ( interface , NULL ) ;
2006-06-22 19:04:47 +02:00
usb_put_dev ( dev - > udev ) ;
dev_info ( & interface - > dev ,
" Cypress CY7C63xxx device now disconnected \n " ) ;
kfree ( dev ) ;
}
static struct usb_driver cypress_driver = {
. name = " cypress_cy7c63 " ,
. probe = cypress_probe ,
. disconnect = cypress_disconnect ,
. id_table = cypress_table ,
} ;
static int __init cypress_init ( void )
{
int result ;
/* register this driver with the USB subsystem */
result = usb_register ( & cypress_driver ) ;
2008-08-14 09:37:34 -07:00
if ( result )
printk ( KERN_ERR KBUILD_MODNAME " : usb_register failed! "
" Error number: %d \n " , result ) ;
2006-06-22 19:04:47 +02:00
return result ;
}
static void __exit cypress_exit ( void )
{
/* deregister this driver with the USB subsystem */
usb_deregister ( & cypress_driver ) ;
}
module_init ( cypress_init ) ;
module_exit ( cypress_exit ) ;
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;