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