18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for RobotFuzz OSIF 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2013 Andrew Lunn <andrew@lunn.ch> 68c2ecf20Sopenharmony_ci * Copyright (c) 2007 Barry Carter <Barry.Carter@robotfuzz.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on the i2c-tiny-usb by 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Copyright (C) 2006 Til Harbaum (Till@Harbaum.org) 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/errno.h> 168c2ecf20Sopenharmony_ci#include <linux/i2c.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/usb.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define OSIFI2C_READ 20 218c2ecf20Sopenharmony_ci#define OSIFI2C_WRITE 21 228c2ecf20Sopenharmony_ci#define OSIFI2C_STOP 22 238c2ecf20Sopenharmony_ci#define OSIFI2C_STATUS 23 248c2ecf20Sopenharmony_ci#define OSIFI2C_SET_BIT_RATE 24 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define STATUS_ADDRESS_ACK 0 278c2ecf20Sopenharmony_ci#define STATUS_ADDRESS_NAK 2 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct osif_priv { 308c2ecf20Sopenharmony_ci struct usb_device *usb_dev; 318c2ecf20Sopenharmony_ci struct usb_interface *interface; 328c2ecf20Sopenharmony_ci struct i2c_adapter adapter; 338c2ecf20Sopenharmony_ci unsigned char status; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int osif_usb_read(struct i2c_adapter *adapter, int cmd, 378c2ecf20Sopenharmony_ci int value, int index, void *data, int len) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct osif_priv *priv = adapter->algo_data; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci return usb_control_msg(priv->usb_dev, usb_rcvctrlpipe(priv->usb_dev, 0), 428c2ecf20Sopenharmony_ci cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | 438c2ecf20Sopenharmony_ci USB_DIR_IN, value, index, data, len, 2000); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int osif_usb_write(struct i2c_adapter *adapter, int cmd, 478c2ecf20Sopenharmony_ci int value, int index, void *data, int len) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci struct osif_priv *priv = adapter->algo_data; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return usb_control_msg(priv->usb_dev, usb_sndctrlpipe(priv->usb_dev, 0), 538c2ecf20Sopenharmony_ci cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 548c2ecf20Sopenharmony_ci value, index, data, len, 2000); 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic int osif_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, 588c2ecf20Sopenharmony_ci int num) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct osif_priv *priv = adapter->algo_data; 618c2ecf20Sopenharmony_ci struct i2c_msg *pmsg; 628c2ecf20Sopenharmony_ci int ret; 638c2ecf20Sopenharmony_ci int i; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 668c2ecf20Sopenharmony_ci pmsg = &msgs[i]; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (pmsg->flags & I2C_M_RD) { 698c2ecf20Sopenharmony_ci ret = osif_usb_read(adapter, OSIFI2C_READ, 708c2ecf20Sopenharmony_ci pmsg->flags, pmsg->addr, 718c2ecf20Sopenharmony_ci pmsg->buf, pmsg->len); 728c2ecf20Sopenharmony_ci if (ret != pmsg->len) { 738c2ecf20Sopenharmony_ci dev_err(&adapter->dev, "failure reading data\n"); 748c2ecf20Sopenharmony_ci return -EREMOTEIO; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci } else { 778c2ecf20Sopenharmony_ci ret = osif_usb_write(adapter, OSIFI2C_WRITE, 788c2ecf20Sopenharmony_ci pmsg->flags, pmsg->addr, 798c2ecf20Sopenharmony_ci pmsg->buf, pmsg->len); 808c2ecf20Sopenharmony_ci if (ret != pmsg->len) { 818c2ecf20Sopenharmony_ci dev_err(&adapter->dev, "failure writing data\n"); 828c2ecf20Sopenharmony_ci return -EREMOTEIO; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci ret = osif_usb_write(adapter, OSIFI2C_STOP, 0, 0, NULL, 0); 878c2ecf20Sopenharmony_ci if (ret) { 888c2ecf20Sopenharmony_ci dev_err(&adapter->dev, "failure sending STOP\n"); 898c2ecf20Sopenharmony_ci return -EREMOTEIO; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* read status */ 938c2ecf20Sopenharmony_ci ret = osif_usb_read(adapter, OSIFI2C_STATUS, 0, 0, 948c2ecf20Sopenharmony_ci &priv->status, 1); 958c2ecf20Sopenharmony_ci if (ret != 1) { 968c2ecf20Sopenharmony_ci dev_err(&adapter->dev, "failure reading status\n"); 978c2ecf20Sopenharmony_ci return -EREMOTEIO; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (priv->status != STATUS_ADDRESS_ACK) { 1018c2ecf20Sopenharmony_ci dev_dbg(&adapter->dev, "status = %d\n", priv->status); 1028c2ecf20Sopenharmony_ci return -EREMOTEIO; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return i; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic u32 osif_func(struct i2c_adapter *adapter) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic const struct i2c_algorithm osif_algorithm = { 1158c2ecf20Sopenharmony_ci .master_xfer = osif_xfer, 1168c2ecf20Sopenharmony_ci .functionality = osif_func, 1178c2ecf20Sopenharmony_ci}; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci#define USB_OSIF_VENDOR_ID 0x1964 1208c2ecf20Sopenharmony_ci#define USB_OSIF_PRODUCT_ID 0x0001 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic const struct usb_device_id osif_table[] = { 1238c2ecf20Sopenharmony_ci { USB_DEVICE(USB_OSIF_VENDOR_ID, USB_OSIF_PRODUCT_ID) }, 1248c2ecf20Sopenharmony_ci { } 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, osif_table); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int osif_probe(struct usb_interface *interface, 1298c2ecf20Sopenharmony_ci const struct usb_device_id *id) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci int ret; 1328c2ecf20Sopenharmony_ci struct osif_priv *priv; 1338c2ecf20Sopenharmony_ci u16 version; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci priv = devm_kzalloc(&interface->dev, sizeof(*priv), GFP_KERNEL); 1368c2ecf20Sopenharmony_ci if (!priv) 1378c2ecf20Sopenharmony_ci return -ENOMEM; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci priv->usb_dev = usb_get_dev(interface_to_usbdev(interface)); 1408c2ecf20Sopenharmony_ci priv->interface = interface; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci usb_set_intfdata(interface, priv); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci priv->adapter.owner = THIS_MODULE; 1458c2ecf20Sopenharmony_ci priv->adapter.class = I2C_CLASS_HWMON; 1468c2ecf20Sopenharmony_ci priv->adapter.algo = &osif_algorithm; 1478c2ecf20Sopenharmony_ci priv->adapter.algo_data = priv; 1488c2ecf20Sopenharmony_ci snprintf(priv->adapter.name, sizeof(priv->adapter.name), 1498c2ecf20Sopenharmony_ci "OSIF at bus %03d device %03d", 1508c2ecf20Sopenharmony_ci priv->usb_dev->bus->busnum, priv->usb_dev->devnum); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* 1538c2ecf20Sopenharmony_ci * Set bus frequency. The frequency is: 1548c2ecf20Sopenharmony_ci * 120,000,000 / ( 16 + 2 * div * 4^prescale). 1558c2ecf20Sopenharmony_ci * Using dev = 52, prescale = 0 give 100KHz */ 1568c2ecf20Sopenharmony_ci ret = osif_usb_write(&priv->adapter, OSIFI2C_SET_BIT_RATE, 52, 0, 1578c2ecf20Sopenharmony_ci NULL, 0); 1588c2ecf20Sopenharmony_ci if (ret) { 1598c2ecf20Sopenharmony_ci dev_err(&interface->dev, "failure sending bit rate"); 1608c2ecf20Sopenharmony_ci usb_put_dev(priv->usb_dev); 1618c2ecf20Sopenharmony_ci return ret; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci i2c_add_adapter(&(priv->adapter)); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci version = le16_to_cpu(priv->usb_dev->descriptor.bcdDevice); 1678c2ecf20Sopenharmony_ci dev_info(&interface->dev, 1688c2ecf20Sopenharmony_ci "version %x.%02x found at bus %03d address %03d", 1698c2ecf20Sopenharmony_ci version >> 8, version & 0xff, 1708c2ecf20Sopenharmony_ci priv->usb_dev->bus->busnum, priv->usb_dev->devnum); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return 0; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic void osif_disconnect(struct usb_interface *interface) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct osif_priv *priv = usb_get_intfdata(interface); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci i2c_del_adapter(&(priv->adapter)); 1808c2ecf20Sopenharmony_ci usb_set_intfdata(interface, NULL); 1818c2ecf20Sopenharmony_ci usb_put_dev(priv->usb_dev); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic struct usb_driver osif_driver = { 1858c2ecf20Sopenharmony_ci .name = "RobotFuzz Open Source InterFace, OSIF", 1868c2ecf20Sopenharmony_ci .probe = osif_probe, 1878c2ecf20Sopenharmony_ci .disconnect = osif_disconnect, 1888c2ecf20Sopenharmony_ci .id_table = osif_table, 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cimodule_usb_driver(osif_driver); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>"); 1948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Barry Carter <barry.carter@robotfuzz.com>"); 1958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("RobotFuzz OSIF driver"); 1968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 197