]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/dev/smbus/smb.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / dev / smbus / smb.c
1 /*-
2  * Copyright (c) 1998, 2001 Nicolas Souchu
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #ifdef HAVE_KERNEL_OPTION_HEADERS
30 #include "opt_compat.h"
31 #endif
32
33 #include <sys/param.h>
34 #include <sys/kernel.h>
35 #include <sys/systm.h>
36 #include <sys/module.h>
37 #include <sys/bus.h>
38 #include <sys/conf.h>
39 #include <sys/uio.h>
40 #include <sys/fcntl.h>
41
42 #include <dev/smbus/smbconf.h>
43 #include <dev/smbus/smbus.h>
44 #include <dev/smbus/smb.h>
45
46 #include "smbus_if.h"
47
48 #define BUFSIZE 1024
49
50 struct smb_softc {
51
52         int sc_count;                   /* >0 if device opened */
53         struct cdev *sc_devnode;
54 };
55
56 #define IIC_SOFTC(unit) \
57         ((struct smb_softc *)devclass_get_softc(smb_devclass, (unit)))
58
59 #define IIC_DEVICE(unit) \
60         (devclass_get_device(smb_devclass, (unit)))
61
62 static void smb_identify(driver_t *driver, device_t parent);
63 static int smb_probe(device_t);
64 static int smb_attach(device_t);
65 static int smb_detach(device_t);
66
67 static devclass_t smb_devclass;
68
69 static device_method_t smb_methods[] = {
70         /* device interface */
71         DEVMETHOD(device_identify,      smb_identify),
72         DEVMETHOD(device_probe,         smb_probe),
73         DEVMETHOD(device_attach,        smb_attach),
74         DEVMETHOD(device_detach,        smb_detach),
75
76         /* smbus interface */
77         DEVMETHOD(smbus_intr,           smbus_generic_intr),
78
79         { 0, 0 }
80 };
81
82 static driver_t smb_driver = {
83         "smb",
84         smb_methods,
85         sizeof(struct smb_softc),
86 };
87
88 static  d_open_t        smbopen;
89 static  d_close_t       smbclose;
90 static  d_ioctl_t       smbioctl;
91
92 static struct cdevsw smb_cdevsw = {
93         .d_version =    D_VERSION,
94         .d_flags =      D_NEEDGIANT,
95         .d_open =       smbopen,
96         .d_close =      smbclose,
97         .d_ioctl =      smbioctl,
98         .d_name =       "smb",
99 };
100
101 static void
102 smb_identify(driver_t *driver, device_t parent)
103 {
104
105         if (device_find_child(parent, "smb", -1) == NULL)
106                 BUS_ADD_CHILD(parent, 0, "smb", -1);
107 }
108
109 static int
110 smb_probe(device_t dev)
111 {
112         device_set_desc(dev, "SMBus generic I/O");
113
114         return (0);
115 }
116         
117 static int
118 smb_attach(device_t dev)
119 {
120         struct smb_softc *sc = (struct smb_softc *)device_get_softc(dev);
121
122         if (!sc)
123                 return (ENOMEM);
124
125         bzero(sc, sizeof(struct smb_softc *));
126
127         sc->sc_devnode = make_dev(&smb_cdevsw, device_get_unit(dev),
128                         UID_ROOT, GID_WHEEL,
129                         0600, "smb%d", device_get_unit(dev));
130
131         return (0);
132 }
133
134 static int
135 smb_detach(device_t dev)
136 {
137         struct smb_softc *sc = (struct smb_softc *)device_get_softc(dev);
138
139         if (sc->sc_devnode)
140                 destroy_dev(sc->sc_devnode);
141
142         return (0);
143 }
144
145 static int
146 smbopen (struct cdev *dev, int flags, int fmt, struct thread *td)
147 {
148         struct smb_softc *sc = IIC_SOFTC(minor(dev));
149
150         if (sc == NULL)
151                 return (ENXIO);
152
153         if (sc->sc_count != 0)
154                 return (EBUSY);
155
156         sc->sc_count++;
157
158         return (0);
159 }
160
161 static int
162 smbclose(struct cdev *dev, int flags, int fmt, struct thread *td)
163 {
164         struct smb_softc *sc = IIC_SOFTC(minor(dev));
165
166         if (sc == NULL)
167                 return (ENXIO);
168
169         if (sc->sc_count == 0)
170                 /* This is not supposed to happen. */
171                 return (0);
172
173         sc->sc_count--;
174
175         return (0);
176 }
177
178 static int
179 smbioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td)
180 {
181         char buf[SMB_MAXBLOCKSIZE];
182         device_t parent;
183         struct smbcmd *s = (struct smbcmd *)data;
184         struct smb_softc *sc = IIC_SOFTC(minor(dev));
185         device_t smbdev = IIC_DEVICE(minor(dev));
186         int error;
187         short w;
188         u_char count;
189         char c;
190
191         if (sc == NULL)
192                 return (ENXIO);
193         if (s == NULL)
194                 return (EINVAL);
195
196         parent = device_get_parent(smbdev);
197
198         /* Allocate the bus. */
199         if ((error = smbus_request_bus(parent, smbdev,
200                         (flags & O_NONBLOCK) ? SMB_DONTWAIT : (SMB_WAIT | SMB_INTR))))
201                 return (error);
202
203         switch (cmd) {
204         case SMB_QUICK_WRITE:
205                 error = smbus_error(smbus_quick(parent, s->slave, SMB_QWRITE));
206                 break;
207
208         case SMB_QUICK_READ:
209                 error = smbus_error(smbus_quick(parent, s->slave, SMB_QREAD));
210                 break;
211
212         case SMB_SENDB:
213                 error = smbus_error(smbus_sendb(parent, s->slave, s->cmd));
214                 break;
215
216         case SMB_RECVB:
217                 error = smbus_error(smbus_recvb(parent, s->slave, &s->cmd));
218                 break;
219
220         case SMB_WRITEB:
221                 error = smbus_error(smbus_writeb(parent, s->slave, s->cmd,
222                                                 s->data.byte));
223                 break;
224
225         case SMB_WRITEW:
226                 error = smbus_error(smbus_writew(parent, s->slave,
227                                                 s->cmd, s->data.word));
228                 break;
229
230         case SMB_READB:
231                 if (s->data.byte_ptr) {
232                         error = smbus_error(smbus_readb(parent, s->slave,
233                                                 s->cmd, &c));
234                         if (error)
235                                 break;
236                         error = copyout(&c, s->data.byte_ptr,
237                                         sizeof(*(s->data.byte_ptr)));
238                 }
239                 break;
240
241         case SMB_READW:
242                 if (s->data.word_ptr) {
243                         error = smbus_error(smbus_readw(parent, s->slave,
244                                                 s->cmd, &w));
245                         if (error == 0) {
246                                 error = copyout(&w, s->data.word_ptr,
247                                                 sizeof(*(s->data.word_ptr)));
248                         }
249                 }
250                 break;
251
252         case SMB_PCALL:
253                 if (s->data.process.rdata) {
254
255                         error = smbus_error(smbus_pcall(parent, s->slave, s->cmd,
256                                 s->data.process.sdata, &w));
257                         if (error)
258                                 break;
259                         error = copyout(&w, s->data.process.rdata,
260                                         sizeof(*(s->data.process.rdata)));
261                 }
262                 
263                 break;
264
265         case SMB_BWRITE:
266                 if (s->count && s->data.byte_ptr) {
267                         if (s->count > SMB_MAXBLOCKSIZE)
268                                 s->count = SMB_MAXBLOCKSIZE;
269                         error = copyin(s->data.byte_ptr, buf, s->count);
270                         if (error)
271                                 break;
272                         error = smbus_error(smbus_bwrite(parent, s->slave,
273                                                 s->cmd, s->count, buf));
274                 }
275                 break;
276
277 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || defined(COMPAT_FREEBSD6)
278         case SMB_OLD_BREAD:
279 #endif
280         case SMB_BREAD:
281                 if (s->count && s->data.byte_ptr) {
282                         count = min(s->count, SMB_MAXBLOCKSIZE);
283                         error = smbus_error(smbus_bread(parent, s->slave,
284                                                 s->cmd, &count, buf));
285                         if (error)
286                                 break;
287                         error = copyout(buf, s->data.byte_ptr,
288                             min(count, s->count));
289                         s->count = count;
290                 }
291                 break;
292                 
293         default:
294                 error = ENOTTY;
295         }
296
297         smbus_release_bus(parent, smbdev);
298
299         return (error);
300 }
301
302 DRIVER_MODULE(smb, smbus, smb_driver, smb_devclass, 0, 0);
303 MODULE_DEPEND(smb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
304 MODULE_VERSION(smb, 1);