]>
Commit | Line | Data |
---|---|---|
83e53a8f AL |
1 | /* |
2 | * Driver for RobotFuzz OSIF | |
3 | * | |
4 | * Copyright (c) 2013 Andrew Lunn <andrew@lunn.ch> | |
5 | * Copyright (c) 2007 Barry Carter <Barry.Carter@robotfuzz.com> | |
6 | * | |
7 | * Based on the i2c-tiny-usb by | |
8 | * | |
9 | * Copyright (C) 2006 Til Harbaum (Till@Harbaum.org) | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or | |
12 | * modify it under the terms of the GNU General Public License as | |
13 | * published by the Free Software Foundation, version 2. | |
14 | */ | |
15 | ||
16 | #include <linux/kernel.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/errno.h> | |
19 | #include <linux/i2c.h> | |
20 | #include <linux/slab.h> | |
21 | #include <linux/usb.h> | |
22 | ||
23 | #define OSIFI2C_READ 20 | |
24 | #define OSIFI2C_WRITE 21 | |
25 | #define OSIFI2C_STOP 22 | |
26 | #define OSIFI2C_STATUS 23 | |
27 | #define OSIFI2C_SET_BIT_RATE 24 | |
28 | ||
29 | #define STATUS_ADDRESS_ACK 0 | |
30 | #define STATUS_ADDRESS_NAK 2 | |
31 | ||
32 | struct osif_priv { | |
33 | struct usb_device *usb_dev; | |
34 | struct usb_interface *interface; | |
35 | struct i2c_adapter adapter; | |
36 | unsigned char status; | |
37 | }; | |
38 | ||
39 | static int osif_usb_read(struct i2c_adapter *adapter, int cmd, | |
40 | int value, int index, void *data, int len) | |
41 | { | |
42 | struct osif_priv *priv = adapter->algo_data; | |
43 | ||
44 | return usb_control_msg(priv->usb_dev, usb_rcvctrlpipe(priv->usb_dev, 0), | |
45 | cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | | |
46 | USB_DIR_IN, value, index, data, len, 2000); | |
47 | } | |
48 | ||
49 | static int osif_usb_write(struct i2c_adapter *adapter, int cmd, | |
50 | int value, int index, void *data, int len) | |
51 | { | |
52 | ||
53 | struct osif_priv *priv = adapter->algo_data; | |
54 | ||
55 | return usb_control_msg(priv->usb_dev, usb_sndctrlpipe(priv->usb_dev, 0), | |
56 | cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE, | |
57 | value, index, data, len, 2000); | |
58 | } | |
59 | ||
60 | static int osif_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, | |
61 | int num) | |
62 | { | |
63 | struct osif_priv *priv = adapter->algo_data; | |
64 | struct i2c_msg *pmsg; | |
65 | int ret = 0; | |
66 | int i, cmd; | |
67 | ||
68 | for (i = 0; ret >= 0 && i < num; i++) { | |
69 | pmsg = &msgs[i]; | |
70 | ||
71 | if (pmsg->flags & I2C_M_RD) { | |
72 | cmd = OSIFI2C_READ; | |
73 | ||
74 | ret = osif_usb_read(adapter, cmd, pmsg->flags, | |
75 | pmsg->addr, pmsg->buf, | |
76 | pmsg->len); | |
77 | if (ret != pmsg->len) { | |
78 | dev_err(&adapter->dev, "failure reading data\n"); | |
79 | return -EREMOTEIO; | |
80 | } | |
81 | } else { | |
82 | cmd = OSIFI2C_WRITE; | |
83 | ||
84 | ret = osif_usb_write(adapter, cmd, pmsg->flags, | |
85 | pmsg->addr, pmsg->buf, pmsg->len); | |
86 | if (ret != pmsg->len) { | |
87 | dev_err(&adapter->dev, "failure writing data\n"); | |
88 | return -EREMOTEIO; | |
89 | } | |
90 | } | |
91 | ||
92 | ret = osif_usb_read(adapter, OSIFI2C_STOP, 0, 0, NULL, 0); | |
93 | if (ret) { | |
94 | dev_err(&adapter->dev, "failure sending STOP\n"); | |
95 | return -EREMOTEIO; | |
96 | } | |
97 | ||
98 | /* read status */ | |
99 | ret = osif_usb_read(adapter, OSIFI2C_STATUS, 0, 0, | |
100 | &priv->status, 1); | |
101 | if (ret != 1) { | |
102 | dev_err(&adapter->dev, "failure reading status\n"); | |
103 | return -EREMOTEIO; | |
104 | } | |
105 | ||
106 | if (priv->status != STATUS_ADDRESS_ACK) { | |
107 | dev_dbg(&adapter->dev, "status = %d\n", priv->status); | |
108 | return -EREMOTEIO; | |
109 | } | |
110 | } | |
111 | ||
112 | return i; | |
113 | } | |
114 | ||
115 | static u32 osif_func(struct i2c_adapter *adapter) | |
116 | { | |
117 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; | |
118 | } | |
119 | ||
120 | static struct i2c_algorithm osif_algorithm = { | |
121 | .master_xfer = osif_xfer, | |
122 | .functionality = osif_func, | |
123 | }; | |
124 | ||
125 | #define USB_OSIF_VENDOR_ID 0x1964 | |
126 | #define USB_OSIF_PRODUCT_ID 0x0001 | |
127 | ||
128 | static struct usb_device_id osif_table[] = { | |
129 | { USB_DEVICE(USB_OSIF_VENDOR_ID, USB_OSIF_PRODUCT_ID) }, | |
130 | { } | |
131 | }; | |
132 | MODULE_DEVICE_TABLE(usb, osif_table); | |
133 | ||
134 | static int osif_probe(struct usb_interface *interface, | |
135 | const struct usb_device_id *id) | |
136 | { | |
137 | int ret; | |
138 | struct osif_priv *priv; | |
139 | u16 version; | |
140 | ||
141 | priv = devm_kzalloc(&interface->dev, sizeof(*priv), GFP_KERNEL); | |
142 | if (!priv) | |
143 | return -ENOMEM; | |
144 | ||
145 | priv->usb_dev = usb_get_dev(interface_to_usbdev(interface)); | |
146 | priv->interface = interface; | |
147 | ||
148 | usb_set_intfdata(interface, priv); | |
149 | ||
150 | priv->adapter.owner = THIS_MODULE; | |
151 | priv->adapter.class = I2C_CLASS_HWMON; | |
152 | priv->adapter.algo = &osif_algorithm; | |
153 | priv->adapter.algo_data = priv; | |
154 | snprintf(priv->adapter.name, sizeof(priv->adapter.name), | |
155 | "OSIF at bus %03d device %03d", | |
156 | priv->usb_dev->bus->busnum, priv->usb_dev->devnum); | |
157 | ||
158 | /* | |
159 | * Set bus frequency. The frequency is: | |
160 | * 120,000,000 / ( 16 + 2 * div * 4^prescale). | |
161 | * Using dev = 52, prescale = 0 give 100KHz */ | |
162 | ret = osif_usb_read(&priv->adapter, OSIFI2C_SET_BIT_RATE, 52, 0, | |
163 | NULL, 0); | |
164 | if (ret) { | |
165 | dev_err(&interface->dev, "failure sending bit rate"); | |
166 | usb_put_dev(priv->usb_dev); | |
167 | return ret; | |
168 | } | |
169 | ||
170 | i2c_add_adapter(&(priv->adapter)); | |
171 | ||
172 | version = le16_to_cpu(priv->usb_dev->descriptor.bcdDevice); | |
173 | dev_info(&interface->dev, | |
174 | "version %x.%02x found at bus %03d address %03d", | |
175 | version >> 8, version & 0xff, | |
176 | priv->usb_dev->bus->busnum, priv->usb_dev->devnum); | |
177 | ||
178 | return 0; | |
179 | } | |
180 | ||
181 | static void osif_disconnect(struct usb_interface *interface) | |
182 | { | |
183 | struct osif_priv *priv = usb_get_intfdata(interface); | |
184 | ||
185 | i2c_del_adapter(&(priv->adapter)); | |
186 | usb_set_intfdata(interface, NULL); | |
187 | usb_put_dev(priv->usb_dev); | |
188 | } | |
189 | ||
190 | static struct usb_driver osif_driver = { | |
191 | .name = "RobotFuzz Open Source InterFace, OSIF", | |
192 | .probe = osif_probe, | |
193 | .disconnect = osif_disconnect, | |
194 | .id_table = osif_table, | |
195 | }; | |
196 | ||
197 | module_usb_driver(osif_driver); | |
198 | ||
199 | MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>"); | |
200 | MODULE_AUTHOR("Barry Carter <barry.carter@robotfuzz.com>"); | |
201 | MODULE_DESCRIPTION("RobotFuzz OSIF driver"); | |
202 | MODULE_LICENSE("GPL v2"); |