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