Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2/* -*- linux-c -*-
3 * Cypress USB Thermometer driver
4 *
5 * Copyright (c) 2004 Erik Rigtorp <erkki@linux.nu> <erik@rigtorp.com>
6 *
7 * This driver works with Elektor magazine USB Interface as published in
8 * issue #291. It should also work with the original starter kit/demo board
9 * from Cypress.
10 */
11
12
13#include <linux/kernel.h>
14#include <linux/errno.h>
15#include <linux/slab.h>
16#include <linux/module.h>
17#include <linux/usb.h>
18
19#define DRIVER_AUTHOR "Erik Rigtorp"
20#define DRIVER_DESC "Cypress USB Thermometer driver"
21
22#define USB_SKEL_VENDOR_ID 0x04b4
23#define USB_SKEL_PRODUCT_ID 0x0002
24
25static const struct usb_device_id id_table[] = {
26 { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
27 { }
28};
29MODULE_DEVICE_TABLE (usb, id_table);
30
31/* Structure to hold all of our device specific stuff */
32struct usb_cytherm {
33 struct usb_device *udev; /* save off the usb device pointer */
34 struct usb_interface *interface; /* the interface for this device */
35 int brightness;
36};
37
38
39/* local function prototypes */
40static int cytherm_probe(struct usb_interface *interface,
41 const struct usb_device_id *id);
42static void cytherm_disconnect(struct usb_interface *interface);
43
44
45/* usb specific object needed to register this driver with the usb subsystem */
46static struct usb_driver cytherm_driver = {
47 .name = "cytherm",
48 .probe = cytherm_probe,
49 .disconnect = cytherm_disconnect,
50 .id_table = id_table,
51};
52
53/* Vendor requests */
54/* They all operate on one byte at a time */
55#define PING 0x00
56#define READ_ROM 0x01 /* Reads form ROM, value = address */
57#define READ_RAM 0x02 /* Reads form RAM, value = address */
58#define WRITE_RAM 0x03 /* Write to RAM, value = address, index = data */
59#define READ_PORT 0x04 /* Reads from port, value = address */
60#define WRITE_PORT 0x05 /* Write to port, value = address, index = data */
61
62
63/* Send a vendor command to device */
64static int vendor_command(struct usb_device *dev, unsigned char request,
65 unsigned char value, unsigned char index,
66 void *buf, int size)
67{
68 return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
69 request,
70 USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
71 value,
72 index, buf, size,
73 USB_CTRL_GET_TIMEOUT);
74}
75
76
77
78#define BRIGHTNESS 0x2c /* RAM location for brightness value */
79#define BRIGHTNESS_SEM 0x2b /* RAM location for brightness semaphore */
80
81static ssize_t show_brightness(struct device *dev, struct device_attribute *attr, char *buf)
82{
83 struct usb_interface *intf = to_usb_interface(dev);
84 struct usb_cytherm *cytherm = usb_get_intfdata(intf);
85
86 return sprintf(buf, "%i", cytherm->brightness);
87}
88
89static ssize_t set_brightness(struct device *dev, struct device_attribute *attr, const char *buf,
90 size_t count)
91{
92 struct usb_interface *intf = to_usb_interface(dev);
93 struct usb_cytherm *cytherm = usb_get_intfdata(intf);
94
95 unsigned char *buffer;
96 int retval;
97
98 buffer = kmalloc(8, GFP_KERNEL);
99 if (!buffer)
100 return 0;
101
102 cytherm->brightness = simple_strtoul(buf, NULL, 10);
103
104 if (cytherm->brightness > 0xFF)
105 cytherm->brightness = 0xFF;
106 else if (cytherm->brightness < 0)
107 cytherm->brightness = 0;
108
109 /* Set brightness */
110 retval = vendor_command(cytherm->udev, WRITE_RAM, BRIGHTNESS,
111 cytherm->brightness, buffer, 8);
112 if (retval)
113 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
114 /* Inform µC that we have changed the brightness setting */
115 retval = vendor_command(cytherm->udev, WRITE_RAM, BRIGHTNESS_SEM,
116 0x01, buffer, 8);
117 if (retval)
118 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
119
120 kfree(buffer);
121
122 return count;
123}
124
125static DEVICE_ATTR(brightness, S_IRUGO | S_IWUSR | S_IWGRP,
126 show_brightness, set_brightness);
127
128
129#define TEMP 0x33 /* RAM location for temperature */
130#define SIGN 0x34 /* RAM location for temperature sign */
131
132static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf)
133{
134
135 struct usb_interface *intf = to_usb_interface(dev);
136 struct usb_cytherm *cytherm = usb_get_intfdata(intf);
137
138 int retval;
139 unsigned char *buffer;
140
141 int temp, sign;
142
143 buffer = kmalloc(8, GFP_KERNEL);
144 if (!buffer)
145 return 0;
146
147 /* read temperature */
148 retval = vendor_command(cytherm->udev, READ_RAM, TEMP, 0, buffer, 8);
149 if (retval)
150 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
151 temp = buffer[1];
152
153 /* read sign */
154 retval = vendor_command(cytherm->udev, READ_RAM, SIGN, 0, buffer, 8);
155 if (retval)
156 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
157 sign = buffer[1];
158
159 kfree(buffer);
160
161 return sprintf(buf, "%c%i.%i", sign ? '-' : '+', temp >> 1,
162 5*(temp - ((temp >> 1) << 1)));
163}
164
165
166static ssize_t set_temp(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
167{
168 return count;
169}
170
171static DEVICE_ATTR(temp, S_IRUGO, show_temp, set_temp);
172
173
174#define BUTTON 0x7a
175
176static ssize_t show_button(struct device *dev, struct device_attribute *attr, char *buf)
177{
178
179 struct usb_interface *intf = to_usb_interface(dev);
180 struct usb_cytherm *cytherm = usb_get_intfdata(intf);
181
182 int retval;
183 unsigned char *buffer;
184
185 buffer = kmalloc(8, GFP_KERNEL);
186 if (!buffer)
187 return 0;
188
189 /* check button */
190 retval = vendor_command(cytherm->udev, READ_RAM, BUTTON, 0, buffer, 8);
191 if (retval)
192 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
193
194 retval = buffer[1];
195
196 kfree(buffer);
197
198 if (retval)
199 return sprintf(buf, "1");
200 else
201 return sprintf(buf, "0");
202}
203
204
205static ssize_t set_button(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
206{
207 return count;
208}
209
210static DEVICE_ATTR(button, S_IRUGO, show_button, set_button);
211
212
213static ssize_t show_port0(struct device *dev, struct device_attribute *attr, char *buf)
214{
215 struct usb_interface *intf = to_usb_interface(dev);
216 struct usb_cytherm *cytherm = usb_get_intfdata(intf);
217
218 int retval;
219 unsigned char *buffer;
220
221 buffer = kmalloc(8, GFP_KERNEL);
222 if (!buffer)
223 return 0;
224
225 retval = vendor_command(cytherm->udev, READ_PORT, 0, 0, buffer, 8);
226 if (retval)
227 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
228
229 retval = buffer[1];
230
231 kfree(buffer);
232
233 return sprintf(buf, "%d", retval);
234}
235
236
237static ssize_t set_port0(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
238{
239 struct usb_interface *intf = to_usb_interface(dev);
240 struct usb_cytherm *cytherm = usb_get_intfdata(intf);
241
242 unsigned char *buffer;
243 int retval;
244 int tmp;
245
246 buffer = kmalloc(8, GFP_KERNEL);
247 if (!buffer)
248 return 0;
249
250 tmp = simple_strtoul(buf, NULL, 10);
251
252 if (tmp > 0xFF)
253 tmp = 0xFF;
254 else if (tmp < 0)
255 tmp = 0;
256
257 retval = vendor_command(cytherm->udev, WRITE_PORT, 0,
258 tmp, buffer, 8);
259 if (retval)
260 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
261
262 kfree(buffer);
263
264 return count;
265}
266
267static DEVICE_ATTR(port0, S_IRUGO | S_IWUSR | S_IWGRP, show_port0, set_port0);
268
269static ssize_t show_port1(struct device *dev, struct device_attribute *attr, char *buf)
270{
271 struct usb_interface *intf = to_usb_interface(dev);
272 struct usb_cytherm *cytherm = usb_get_intfdata(intf);
273
274 int retval;
275 unsigned char *buffer;
276
277 buffer = kmalloc(8, GFP_KERNEL);
278 if (!buffer)
279 return 0;
280
281 retval = vendor_command(cytherm->udev, READ_PORT, 1, 0, buffer, 8);
282 if (retval)
283 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
284
285 retval = buffer[1];
286
287 kfree(buffer);
288
289 return sprintf(buf, "%d", retval);
290}
291
292
293static ssize_t set_port1(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
294{
295 struct usb_interface *intf = to_usb_interface(dev);
296 struct usb_cytherm *cytherm = usb_get_intfdata(intf);
297
298 unsigned char *buffer;
299 int retval;
300 int tmp;
301
302 buffer = kmalloc(8, GFP_KERNEL);
303 if (!buffer)
304 return 0;
305
306 tmp = simple_strtoul(buf, NULL, 10);
307
308 if (tmp > 0xFF)
309 tmp = 0xFF;
310 else if (tmp < 0)
311 tmp = 0;
312
313 retval = vendor_command(cytherm->udev, WRITE_PORT, 1,
314 tmp, buffer, 8);
315 if (retval)
316 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
317
318 kfree(buffer);
319
320 return count;
321}
322
323static DEVICE_ATTR(port1, S_IRUGO | S_IWUSR | S_IWGRP, show_port1, set_port1);
324
325
326
327static int cytherm_probe(struct usb_interface *interface,
328 const struct usb_device_id *id)
329{
330 struct usb_device *udev = interface_to_usbdev(interface);
331 struct usb_cytherm *dev = NULL;
332 int retval = -ENOMEM;
333
334 dev = kzalloc (sizeof(struct usb_cytherm), GFP_KERNEL);
335 if (!dev)
336 goto error_mem;
337
338 dev->udev = usb_get_dev(udev);
339
340 usb_set_intfdata (interface, dev);
341
342 dev->brightness = 0xFF;
343
344 retval = device_create_file(&interface->dev, &dev_attr_brightness);
345 if (retval)
346 goto error;
347 retval = device_create_file(&interface->dev, &dev_attr_temp);
348 if (retval)
349 goto error;
350 retval = device_create_file(&interface->dev, &dev_attr_button);
351 if (retval)
352 goto error;
353 retval = device_create_file(&interface->dev, &dev_attr_port0);
354 if (retval)
355 goto error;
356 retval = device_create_file(&interface->dev, &dev_attr_port1);
357 if (retval)
358 goto error;
359
360 dev_info (&interface->dev,
361 "Cypress thermometer device now attached\n");
362 return 0;
363error:
364 device_remove_file(&interface->dev, &dev_attr_brightness);
365 device_remove_file(&interface->dev, &dev_attr_temp);
366 device_remove_file(&interface->dev, &dev_attr_button);
367 device_remove_file(&interface->dev, &dev_attr_port0);
368 device_remove_file(&interface->dev, &dev_attr_port1);
369 usb_set_intfdata (interface, NULL);
370 usb_put_dev(dev->udev);
371 kfree(dev);
372error_mem:
373 return retval;
374}
375
376static void cytherm_disconnect(struct usb_interface *interface)
377{
378 struct usb_cytherm *dev;
379
380 dev = usb_get_intfdata (interface);
381
382 device_remove_file(&interface->dev, &dev_attr_brightness);
383 device_remove_file(&interface->dev, &dev_attr_temp);
384 device_remove_file(&interface->dev, &dev_attr_button);
385 device_remove_file(&interface->dev, &dev_attr_port0);
386 device_remove_file(&interface->dev, &dev_attr_port1);
387
388 /* first remove the files, then NULL the pointer */
389 usb_set_intfdata (interface, NULL);
390
391 usb_put_dev(dev->udev);
392
393 kfree(dev);
394
395 dev_info(&interface->dev, "Cypress thermometer now disconnected\n");
396}
397
398module_usb_driver(cytherm_driver);
399
400MODULE_AUTHOR(DRIVER_AUTHOR);
401MODULE_DESCRIPTION(DRIVER_DESC);
402MODULE_LICENSE("GPL");