Rev 458 | Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
420 | giacomo | 1 | /* |
2 | i2c-dev.c - i2c-bus driver, char device interface |
||
3 | |||
4 | Copyright (C) 1995-97 Simon G. Vogl |
||
5 | Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl> |
||
6 | Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> |
||
7 | |||
8 | This program is free software; you can redistribute it and/or modify |
||
9 | it under the terms of the GNU General Public License as published by |
||
10 | the Free Software Foundation; either version 2 of the License, or |
||
11 | (at your option) any later version. |
||
12 | |||
13 | This program is distributed in the hope that it will be useful, |
||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
16 | GNU General Public License for more details. |
||
17 | |||
18 | You should have received a copy of the GNU General Public License |
||
19 | along with this program; if not, write to the Free Software |
||
20 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
||
21 | */ |
||
22 | |||
23 | /* Note that this is a complete rewrite of Simon Vogl's i2c-dev module. |
||
24 | But I have used so much of his original code and ideas that it seems |
||
25 | only fair to recognize him as co-author -- Frodo */ |
||
26 | |||
27 | /* The I2C_RDWR ioctl code is written by Kolja Waschk <waschk@telos.de> */ |
||
28 | |||
29 | /* The devfs code is contributed by Philipp Matthias Hahn |
||
30 | <pmhahn@titan.lahn.de> */ |
||
31 | |||
32 | /* If you want debugging uncomment: */ |
||
33 | /* #define DEBUG 1 */ |
||
34 | |||
35 | #include <linux/kernel.h> |
||
36 | #include <linux/module.h> |
||
37 | #include <linux/fs.h> |
||
38 | #include <linux/slab.h> |
||
39 | #include <linux/smp_lock.h> |
||
40 | #include <linux/devfs_fs_kernel.h> |
||
41 | #include <linux/init.h> |
||
42 | #include <linux/i2c.h> |
||
43 | #include <linux/i2c-dev.h> |
||
44 | #include <asm/uaccess.h> |
||
45 | |||
46 | static struct i2c_client i2cdev_client_template; |
||
47 | |||
48 | struct i2c_dev { |
||
49 | int minor; |
||
50 | struct i2c_adapter *adap; |
||
51 | struct class_device class_dev; |
||
52 | struct completion released; /* FIXME, we need a class_device_unregister() */ |
||
53 | }; |
||
54 | #define to_i2c_dev(d) container_of(d, struct i2c_dev, class_dev) |
||
55 | |||
56 | #define I2C_MINORS 256 |
||
57 | static struct i2c_dev *i2c_dev_array[I2C_MINORS]; |
||
58 | static spinlock_t i2c_dev_array_lock = SPIN_LOCK_UNLOCKED; |
||
59 | |||
60 | struct i2c_dev *i2c_dev_get_by_minor(unsigned index) |
||
61 | { |
||
62 | struct i2c_dev *i2c_dev; |
||
63 | |||
64 | spin_lock(&i2c_dev_array_lock); |
||
65 | i2c_dev = i2c_dev_array[index]; |
||
66 | spin_unlock(&i2c_dev_array_lock); |
||
67 | return i2c_dev; |
||
68 | } |
||
69 | |||
70 | struct i2c_dev *i2c_dev_get_by_adapter(struct i2c_adapter *adap) |
||
71 | { |
||
72 | struct i2c_dev *i2c_dev = NULL; |
||
73 | int i; |
||
74 | |||
75 | spin_lock(&i2c_dev_array_lock); |
||
76 | for (i = 0; i < I2C_MINORS; ++i) { |
||
77 | if ((i2c_dev_array[i]) && |
||
78 | (i2c_dev_array[i]->adap == adap)) { |
||
79 | i2c_dev = i2c_dev_array[i]; |
||
80 | break; |
||
81 | } |
||
82 | } |
||
83 | spin_unlock(&i2c_dev_array_lock); |
||
84 | return i2c_dev; |
||
85 | } |
||
86 | |||
87 | static struct i2c_dev *get_free_i2c_dev(void) |
||
88 | { |
||
89 | struct i2c_dev *i2c_dev; |
||
90 | unsigned int i; |
||
91 | |||
92 | i2c_dev = kmalloc(sizeof(*i2c_dev), GFP_KERNEL); |
||
93 | if (!i2c_dev) |
||
94 | return ERR_PTR(-ENOMEM); |
||
95 | memset(i2c_dev, 0x00, sizeof(*i2c_dev)); |
||
96 | |||
97 | spin_lock(&i2c_dev_array_lock); |
||
98 | for (i = 0; i < I2C_MINORS; ++i) { |
||
99 | if (i2c_dev_array[i]) |
||
100 | continue; |
||
101 | i2c_dev->minor = i; |
||
102 | i2c_dev_array[i] = i2c_dev; |
||
103 | spin_unlock(&i2c_dev_array_lock); |
||
104 | return i2c_dev; |
||
105 | } |
||
106 | spin_unlock(&i2c_dev_array_lock); |
||
107 | kfree(i2c_dev); |
||
108 | return ERR_PTR(-ENODEV); |
||
109 | } |
||
110 | |||
111 | static void return_i2c_dev(struct i2c_dev *i2c_dev) |
||
112 | { |
||
113 | spin_lock(&i2c_dev_array_lock); |
||
114 | i2c_dev_array[i2c_dev->minor] = NULL; |
||
115 | spin_unlock(&i2c_dev_array_lock); |
||
116 | } |
||
117 | |||
118 | static ssize_t show_dev(struct class_device *class_dev, char *buf) |
||
119 | { |
||
120 | struct i2c_dev *i2c_dev = to_i2c_dev(class_dev); |
||
121 | return print_dev_t(buf, MKDEV(I2C_MAJOR, i2c_dev->minor)); |
||
122 | } |
||
123 | static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL); |
||
124 | |||
125 | static ssize_t i2cdev_read (struct file *file, char __user *buf, size_t count, |
||
126 | loff_t *offset) |
||
127 | { |
||
128 | char *tmp; |
||
129 | int ret; |
||
130 | |||
131 | struct i2c_client *client = (struct i2c_client *)file->private_data; |
||
132 | |||
133 | if (count > 8192) |
||
134 | count = 8192; |
||
135 | |||
136 | tmp = kmalloc(count,GFP_KERNEL); |
||
137 | if (tmp==NULL) |
||
138 | return -ENOMEM; |
||
139 | |||
140 | pr_debug("i2c-dev.o: i2c-%d reading %d bytes.\n", |
||
141 | iminor(file->f_dentry->d_inode), count); |
||
142 | |||
143 | ret = i2c_master_recv(client,tmp,count); |
||
144 | if (ret >= 0) |
||
145 | ret = copy_to_user(buf,tmp,count)?-EFAULT:ret; |
||
146 | kfree(tmp); |
||
147 | return ret; |
||
148 | } |
||
149 | |||
150 | static ssize_t i2cdev_write (struct file *file, const char __user *buf, size_t count, |
||
151 | loff_t *offset) |
||
152 | { |
||
153 | int ret; |
||
154 | char *tmp; |
||
155 | struct i2c_client *client = (struct i2c_client *)file->private_data; |
||
156 | |||
157 | if (count > 8192) |
||
158 | count = 8192; |
||
159 | |||
160 | tmp = kmalloc(count,GFP_KERNEL); |
||
161 | if (tmp==NULL) |
||
162 | return -ENOMEM; |
||
163 | if (copy_from_user(tmp,buf,count)) { |
||
164 | kfree(tmp); |
||
165 | return -EFAULT; |
||
166 | } |
||
167 | |||
168 | pr_debug("i2c-dev.o: i2c-%d writing %d bytes.\n", |
||
169 | iminor(file->f_dentry->d_inode), count); |
||
170 | |||
171 | ret = i2c_master_send(client,tmp,count); |
||
172 | kfree(tmp); |
||
173 | return ret; |
||
174 | } |
||
175 | |||
176 | int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, |
||
177 | unsigned long arg) |
||
178 | { |
||
179 | struct i2c_client *client = (struct i2c_client *)file->private_data; |
||
180 | struct i2c_rdwr_ioctl_data rdwr_arg; |
||
181 | struct i2c_smbus_ioctl_data data_arg; |
||
182 | union i2c_smbus_data temp; |
||
183 | struct i2c_msg *rdwr_pa; |
||
184 | u8 **data_ptrs; |
||
185 | int i,datasize,res; |
||
186 | unsigned long funcs; |
||
187 | |||
188 | dev_dbg(&client->dev, "i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", |
||
189 | iminor(inode),cmd, arg); |
||
190 | |||
191 | switch ( cmd ) { |
||
192 | case I2C_SLAVE: |
||
193 | case I2C_SLAVE_FORCE: |
||
194 | if ((arg > 0x3ff) || |
||
195 | (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) |
||
196 | return -EINVAL; |
||
197 | if ((cmd == I2C_SLAVE) && i2c_check_addr(client->adapter,arg)) |
||
198 | return -EBUSY; |
||
199 | client->addr = arg; |
||
200 | return 0; |
||
201 | case I2C_TENBIT: |
||
202 | if (arg) |
||
203 | client->flags |= I2C_M_TEN; |
||
204 | else |
||
205 | client->flags &= ~I2C_M_TEN; |
||
206 | return 0; |
||
207 | case I2C_PEC: |
||
208 | if (arg) |
||
209 | client->flags |= I2C_CLIENT_PEC; |
||
210 | else |
||
211 | client->flags &= ~I2C_CLIENT_PEC; |
||
212 | return 0; |
||
213 | case I2C_FUNCS: |
||
214 | funcs = i2c_get_functionality(client->adapter); |
||
215 | return (copy_to_user((unsigned long __user *)arg, &funcs, |
||
216 | sizeof(unsigned long)))?-EFAULT:0; |
||
217 | |||
218 | case I2C_RDWR: |
||
219 | if (copy_from_user(&rdwr_arg, |
||
220 | (struct i2c_rdwr_ioctl_data __user *)arg, |
||
221 | sizeof(rdwr_arg))) |
||
222 | return -EFAULT; |
||
223 | |||
224 | /* Put an arbritrary limit on the number of messages that can |
||
225 | * be sent at once */ |
||
226 | if (rdwr_arg.nmsgs > 42) |
||
227 | return -EINVAL; |
||
228 | |||
229 | rdwr_pa = (struct i2c_msg *) |
||
230 | kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), |
||
231 | GFP_KERNEL); |
||
232 | |||
233 | if (rdwr_pa == NULL) return -ENOMEM; |
||
234 | |||
235 | if (copy_from_user(rdwr_pa, rdwr_arg.msgs, |
||
236 | rdwr_arg.nmsgs * sizeof(struct i2c_msg))) { |
||
237 | kfree(rdwr_pa); |
||
238 | return -EFAULT; |
||
239 | } |
||
240 | |||
241 | data_ptrs = (u8 **) kmalloc(rdwr_arg.nmsgs * sizeof(u8 *), |
||
242 | GFP_KERNEL); |
||
243 | if (data_ptrs == NULL) { |
||
244 | kfree(rdwr_pa); |
||
245 | return -ENOMEM; |
||
246 | } |
||
247 | |||
248 | res = 0; |
||
249 | for( i=0; i<rdwr_arg.nmsgs; i++ ) { |
||
250 | /* Limit the size of the message to a sane amount */ |
||
251 | if (rdwr_pa[i].len > 8192) { |
||
252 | res = -EINVAL; |
||
253 | break; |
||
254 | } |
||
255 | data_ptrs[i] = rdwr_pa[i].buf; |
||
256 | rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL); |
||
257 | if(rdwr_pa[i].buf == NULL) { |
||
258 | res = -ENOMEM; |
||
259 | break; |
||
260 | } |
||
261 | if(copy_from_user(rdwr_pa[i].buf, |
||
262 | data_ptrs[i], |
||
263 | rdwr_pa[i].len)) { |
||
264 | ++i; /* Needs to be kfreed too */ |
||
265 | res = -EFAULT; |
||
266 | break; |
||
267 | } |
||
268 | } |
||
269 | if (res < 0) { |
||
270 | int j; |
||
271 | for (j = 0; j < i; ++j) |
||
272 | kfree(rdwr_pa[j].buf); |
||
273 | kfree(data_ptrs); |
||
274 | kfree(rdwr_pa); |
||
275 | return res; |
||
276 | } |
||
277 | |||
278 | res = i2c_transfer(client->adapter, |
||
279 | rdwr_pa, |
||
280 | rdwr_arg.nmsgs); |
||
281 | while(i-- > 0) { |
||
282 | if( res>=0 && (rdwr_pa[i].flags & I2C_M_RD)) { |
||
283 | if(copy_to_user( |
||
284 | data_ptrs[i], |
||
285 | rdwr_pa[i].buf, |
||
286 | rdwr_pa[i].len)) { |
||
287 | res = -EFAULT; |
||
288 | } |
||
289 | } |
||
290 | kfree(rdwr_pa[i].buf); |
||
291 | } |
||
292 | kfree(data_ptrs); |
||
293 | kfree(rdwr_pa); |
||
294 | return res; |
||
295 | |||
296 | case I2C_SMBUS: |
||
297 | if (copy_from_user(&data_arg, |
||
298 | (struct i2c_smbus_ioctl_data __user *) arg, |
||
299 | sizeof(struct i2c_smbus_ioctl_data))) |
||
300 | return -EFAULT; |
||
301 | if ((data_arg.size != I2C_SMBUS_BYTE) && |
||
302 | (data_arg.size != I2C_SMBUS_QUICK) && |
||
303 | (data_arg.size != I2C_SMBUS_BYTE_DATA) && |
||
304 | (data_arg.size != I2C_SMBUS_WORD_DATA) && |
||
305 | (data_arg.size != I2C_SMBUS_PROC_CALL) && |
||
306 | (data_arg.size != I2C_SMBUS_BLOCK_DATA) && |
||
307 | (data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA) && |
||
308 | (data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) { |
||
309 | dev_dbg(&client->dev, |
||
310 | "size out of range (%x) in ioctl I2C_SMBUS.\n", |
||
311 | data_arg.size); |
||
312 | return -EINVAL; |
||
313 | } |
||
314 | /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, |
||
315 | so the check is valid if size==I2C_SMBUS_QUICK too. */ |
||
316 | if ((data_arg.read_write != I2C_SMBUS_READ) && |
||
317 | (data_arg.read_write != I2C_SMBUS_WRITE)) { |
||
318 | dev_dbg(&client->dev, |
||
319 | "read_write out of range (%x) in ioctl I2C_SMBUS.\n", |
||
320 | data_arg.read_write); |
||
321 | return -EINVAL; |
||
322 | } |
||
323 | |||
324 | /* Note that command values are always valid! */ |
||
325 | |||
326 | if ((data_arg.size == I2C_SMBUS_QUICK) || |
||
327 | ((data_arg.size == I2C_SMBUS_BYTE) && |
||
328 | (data_arg.read_write == I2C_SMBUS_WRITE))) |
||
329 | /* These are special: we do not use data */ |
||
330 | return i2c_smbus_xfer(client->adapter, client->addr, |
||
331 | client->flags, |
||
332 | data_arg.read_write, |
||
333 | data_arg.command, |
||
334 | data_arg.size, NULL); |
||
335 | |||
336 | if (data_arg.data == NULL) { |
||
337 | dev_dbg(&client->dev, |
||
338 | "data is NULL pointer in ioctl I2C_SMBUS.\n"); |
||
339 | return -EINVAL; |
||
340 | } |
||
341 | |||
342 | if ((data_arg.size == I2C_SMBUS_BYTE_DATA) || |
||
343 | (data_arg.size == I2C_SMBUS_BYTE)) |
||
344 | datasize = sizeof(data_arg.data->byte); |
||
345 | else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || |
||
346 | (data_arg.size == I2C_SMBUS_PROC_CALL)) |
||
347 | datasize = sizeof(data_arg.data->word); |
||
348 | else /* size == smbus block, i2c block, or block proc. call */ |
||
349 | datasize = sizeof(data_arg.data->block); |
||
350 | |||
351 | if ((data_arg.size == I2C_SMBUS_PROC_CALL) || |
||
352 | (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || |
||
353 | (data_arg.read_write == I2C_SMBUS_WRITE)) { |
||
354 | if (copy_from_user(&temp, data_arg.data, datasize)) |
||
355 | return -EFAULT; |
||
356 | } |
||
357 | res = i2c_smbus_xfer(client->adapter,client->addr,client->flags, |
||
358 | data_arg.read_write, |
||
359 | data_arg.command,data_arg.size,&temp); |
||
360 | if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || |
||
361 | (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || |
||
362 | (data_arg.read_write == I2C_SMBUS_READ))) { |
||
363 | if (copy_to_user(data_arg.data, &temp, datasize)) |
||
364 | return -EFAULT; |
||
365 | } |
||
366 | return res; |
||
367 | |||
368 | default: |
||
369 | return i2c_control(client,cmd,arg); |
||
370 | } |
||
371 | return 0; |
||
372 | } |
||
373 | |||
374 | static int i2cdev_open(struct inode *inode, struct file *file) |
||
375 | { |
||
376 | unsigned int minor = iminor(inode); |
||
377 | struct i2c_client *client; |
||
378 | struct i2c_adapter *adap; |
||
379 | struct i2c_dev *i2c_dev; |
||
380 | |||
381 | i2c_dev = i2c_dev_get_by_minor(minor); |
||
382 | if (!i2c_dev) |
||
383 | return -ENODEV; |
||
384 | |||
385 | adap = i2c_get_adapter(i2c_dev->adap->nr); |
||
386 | if (!adap) |
||
387 | return -ENODEV; |
||
388 | |||
389 | client = kmalloc(sizeof(*client), GFP_KERNEL); |
||
390 | if (!client) { |
||
391 | i2c_put_adapter(adap); |
||
392 | return -ENOMEM; |
||
393 | } |
||
394 | memcpy(client, &i2cdev_client_template, sizeof(*client)); |
||
395 | |||
396 | /* registered with adapter, passed as client to user */ |
||
397 | client->adapter = adap; |
||
398 | file->private_data = client; |
||
399 | |||
400 | return 0; |
||
401 | } |
||
402 | |||
403 | static int i2cdev_release(struct inode *inode, struct file *file) |
||
404 | { |
||
405 | struct i2c_client *client = file->private_data; |
||
406 | |||
407 | i2c_put_adapter(client->adapter); |
||
408 | kfree(client); |
||
409 | file->private_data = NULL; |
||
410 | |||
411 | return 0; |
||
412 | } |
||
413 | |||
414 | static struct file_operations i2cdev_fops = { |
||
415 | .owner = THIS_MODULE, |
||
416 | .llseek = no_llseek, |
||
417 | .read = i2cdev_read, |
||
418 | .write = i2cdev_write, |
||
419 | .ioctl = i2cdev_ioctl, |
||
420 | .open = i2cdev_open, |
||
421 | .release = i2cdev_release, |
||
422 | }; |
||
423 | |||
424 | static void release_i2c_dev(struct class_device *dev) |
||
425 | { |
||
426 | struct i2c_dev *i2c_dev = to_i2c_dev(dev); |
||
427 | complete(&i2c_dev->released); |
||
428 | } |
||
429 | |||
430 | static struct class i2c_dev_class = { |
||
431 | .name = "i2c-dev", |
||
432 | .release = &release_i2c_dev, |
||
433 | }; |
||
434 | |||
435 | static int i2cdev_attach_adapter(struct i2c_adapter *adap) |
||
436 | { |
||
437 | struct i2c_dev *i2c_dev; |
||
438 | int retval; |
||
439 | |||
440 | i2c_dev = get_free_i2c_dev(); |
||
441 | if (IS_ERR(i2c_dev)) |
||
442 | return PTR_ERR(i2c_dev); |
||
443 | |||
444 | devfs_mk_cdev(MKDEV(I2C_MAJOR, i2c_dev->minor), |
||
445 | S_IFCHR|S_IRUSR|S_IWUSR, "i2c/%d", i2c_dev->minor); |
||
446 | dev_dbg(&adap->dev, "Registered as minor %d\n", i2c_dev->minor); |
||
447 | |||
448 | /* register this i2c device with the driver core */ |
||
449 | i2c_dev->adap = adap; |
||
450 | if (adap->dev.parent == &legacy_bus) |
||
451 | i2c_dev->class_dev.dev = &adap->dev; |
||
452 | else |
||
453 | i2c_dev->class_dev.dev = adap->dev.parent; |
||
454 | i2c_dev->class_dev.class = &i2c_dev_class; |
||
455 | snprintf(i2c_dev->class_dev.class_id, BUS_ID_SIZE, "i2c-%d", i2c_dev->minor); |
||
456 | retval = class_device_register(&i2c_dev->class_dev); |
||
457 | if (retval) |
||
458 | goto error; |
||
459 | class_device_create_file(&i2c_dev->class_dev, &class_device_attr_dev); |
||
460 | return 0; |
||
461 | error: |
||
462 | return_i2c_dev(i2c_dev); |
||
463 | kfree(i2c_dev); |
||
464 | return retval; |
||
465 | } |
||
466 | |||
467 | static int i2cdev_detach_adapter(struct i2c_adapter *adap) |
||
468 | { |
||
469 | struct i2c_dev *i2c_dev; |
||
470 | |||
471 | i2c_dev = i2c_dev_get_by_adapter(adap); |
||
472 | if (!i2c_dev) |
||
473 | return -ENODEV; |
||
474 | |||
475 | init_completion(&i2c_dev->released); |
||
476 | devfs_remove("i2c/%d", i2c_dev->minor); |
||
477 | return_i2c_dev(i2c_dev); |
||
478 | class_device_unregister(&i2c_dev->class_dev); |
||
479 | wait_for_completion(&i2c_dev->released); |
||
480 | kfree(i2c_dev); |
||
481 | |||
482 | dev_dbg(&adap->dev, "Adapter unregistered\n"); |
||
483 | return 0; |
||
484 | } |
||
485 | |||
486 | static int i2cdev_detach_client(struct i2c_client *client) |
||
487 | { |
||
488 | return 0; |
||
489 | } |
||
490 | |||
491 | static int i2cdev_command(struct i2c_client *client, unsigned int cmd, |
||
492 | void *arg) |
||
493 | { |
||
494 | return -1; |
||
495 | } |
||
496 | |||
497 | static struct i2c_driver i2cdev_driver = { |
||
498 | .owner = THIS_MODULE, |
||
499 | .name = "dev_driver", |
||
500 | .id = I2C_DRIVERID_I2CDEV, |
||
501 | .flags = I2C_DF_NOTIFY, |
||
502 | .attach_adapter = i2cdev_attach_adapter, |
||
503 | .detach_adapter = i2cdev_detach_adapter, |
||
504 | .detach_client = i2cdev_detach_client, |
||
505 | .command = i2cdev_command, |
||
506 | }; |
||
507 | |||
508 | static struct i2c_client i2cdev_client_template = { |
||
509 | .name = "I2C /dev entry", |
||
510 | .id = 1, |
||
511 | .addr = -1, |
||
512 | .driver = &i2cdev_driver, |
||
513 | }; |
||
514 | |||
515 | static int __init i2c_dev_init(void) |
||
516 | { |
||
517 | int res; |
||
518 | |||
519 | printk(KERN_INFO "i2c /dev entries driver\n"); |
||
520 | |||
521 | if (register_chrdev(I2C_MAJOR,"i2c",&i2cdev_fops)) { |
||
522 | printk(KERN_ERR "i2c-dev.o: unable to get major %d for i2c bus\n", |
||
523 | I2C_MAJOR); |
||
524 | return -EIO; |
||
525 | } |
||
526 | devfs_mk_dir("i2c"); |
||
527 | class_register(&i2c_dev_class); |
||
528 | if ((res = i2c_add_driver(&i2cdev_driver))) { |
||
529 | printk(KERN_ERR "i2c-dev.o: Driver registration failed, module not inserted.\n"); |
||
530 | devfs_remove("i2c"); |
||
531 | unregister_chrdev(I2C_MAJOR,"i2c"); |
||
532 | return res; |
||
533 | } |
||
534 | return 0; |
||
535 | } |
||
536 | |||
537 | static void __exit i2c_dev_exit(void) |
||
538 | { |
||
539 | i2c_del_driver(&i2cdev_driver); |
||
540 | class_unregister(&i2c_dev_class); |
||
541 | devfs_remove("i2c"); |
||
542 | unregister_chrdev(I2C_MAJOR,"i2c"); |
||
543 | } |
||
544 | |||
545 | MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and " |
||
546 | "Simon G. Vogl <simon@tk.uni-linz.ac.at>"); |
||
547 | MODULE_DESCRIPTION("I2C /dev entries driver"); |
||
548 | MODULE_LICENSE("GPL"); |
||
549 | |||
550 | module_init(i2c_dev_init); |
||
551 | module_exit(i2c_dev_exit); |