diff options
author | Len Brown <len.brown@intel.com> | 2009-01-09 03:39:43 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2009-01-09 03:39:43 -0500 |
commit | b2576e1d4408e134e2188c967b1f28af39cd79d4 (patch) | |
tree | 004f3c82faab760f304ce031d6d2f572e7746a50 /drivers/w1/w1_netlink.c | |
parent | 3cc8a5f4ba91f67bbdb81a43a99281a26aab8d77 (diff) | |
parent | 2150edc6c5cf00f7adb54538b9ea2a3e9cedca3f (diff) |
Merge branch 'linus' into release
Diffstat (limited to 'drivers/w1/w1_netlink.c')
-rw-r--r-- | drivers/w1/w1_netlink.c | 261 |
1 files changed, 222 insertions, 39 deletions
diff --git a/drivers/w1/w1_netlink.c b/drivers/w1/w1_netlink.c index 65c5ebd0787..fdf72851c57 100644 --- a/drivers/w1/w1_netlink.c +++ b/drivers/w1/w1_netlink.c | |||
@@ -47,21 +47,56 @@ void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg) | |||
47 | cn_netlink_send(m, 0, GFP_KERNEL); | 47 | cn_netlink_send(m, 0, GFP_KERNEL); |
48 | } | 48 | } |
49 | 49 | ||
50 | static int w1_process_command_master(struct w1_master *dev, struct cn_msg *msg, | 50 | static void w1_send_slave(struct w1_master *dev, u64 rn) |
51 | struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd) | 51 | { |
52 | struct cn_msg *msg = dev->priv; | ||
53 | struct w1_netlink_msg *hdr = (struct w1_netlink_msg *)(msg + 1); | ||
54 | struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)(hdr + 1); | ||
55 | int avail; | ||
56 | |||
57 | avail = dev->priv_size - cmd->len; | ||
58 | |||
59 | if (avail > 8) { | ||
60 | u64 *data = (void *)(cmd + 1) + cmd->len; | ||
61 | |||
62 | *data = rn; | ||
63 | cmd->len += 8; | ||
64 | hdr->len += 8; | ||
65 | msg->len += 8; | ||
66 | return; | ||
67 | } | ||
68 | |||
69 | msg->ack++; | ||
70 | cn_netlink_send(msg, 0, GFP_KERNEL); | ||
71 | |||
72 | msg->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd); | ||
73 | hdr->len = sizeof(struct w1_netlink_cmd); | ||
74 | cmd->len = 0; | ||
75 | } | ||
76 | |||
77 | static int w1_process_search_command(struct w1_master *dev, struct cn_msg *msg, | ||
78 | unsigned int avail) | ||
52 | { | 79 | { |
53 | dev_dbg(&dev->dev, "%s: %s: cmd=%02x, len=%u.\n", | 80 | struct w1_netlink_msg *hdr = (struct w1_netlink_msg *)(msg + 1); |
54 | __func__, dev->name, cmd->cmd, cmd->len); | 81 | struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)(hdr + 1); |
82 | int search_type = (cmd->cmd == W1_CMD_ALARM_SEARCH)?W1_ALARM_SEARCH:W1_SEARCH; | ||
55 | 83 | ||
56 | if (cmd->cmd != W1_CMD_SEARCH && cmd->cmd != W1_CMD_ALARM_SEARCH) | 84 | dev->priv = msg; |
57 | return -EINVAL; | 85 | dev->priv_size = avail; |
86 | |||
87 | w1_search_devices(dev, search_type, w1_send_slave); | ||
88 | |||
89 | msg->ack = 0; | ||
90 | cn_netlink_send(msg, 0, GFP_KERNEL); | ||
91 | |||
92 | dev->priv = NULL; | ||
93 | dev->priv_size = 0; | ||
58 | 94 | ||
59 | w1_search_process(dev, (cmd->cmd == W1_CMD_ALARM_SEARCH)?W1_ALARM_SEARCH:W1_SEARCH); | ||
60 | return 0; | 95 | return 0; |
61 | } | 96 | } |
62 | 97 | ||
63 | static int w1_send_read_reply(struct w1_slave *sl, struct cn_msg *msg, | 98 | static int w1_send_read_reply(struct cn_msg *msg, struct w1_netlink_msg *hdr, |
64 | struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd) | 99 | struct w1_netlink_cmd *cmd) |
65 | { | 100 | { |
66 | void *data; | 101 | void *data; |
67 | struct w1_netlink_msg *h; | 102 | struct w1_netlink_msg *h; |
@@ -85,7 +120,8 @@ static int w1_send_read_reply(struct w1_slave *sl, struct cn_msg *msg, | |||
85 | memcpy(c, cmd, sizeof(struct w1_netlink_cmd)); | 120 | memcpy(c, cmd, sizeof(struct w1_netlink_cmd)); |
86 | 121 | ||
87 | cm->ack = msg->seq+1; | 122 | cm->ack = msg->seq+1; |
88 | cm->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd) + cmd->len; | 123 | cm->len = sizeof(struct w1_netlink_msg) + |
124 | sizeof(struct w1_netlink_cmd) + cmd->len; | ||
89 | 125 | ||
90 | h->len = sizeof(struct w1_netlink_cmd) + cmd->len; | 126 | h->len = sizeof(struct w1_netlink_cmd) + cmd->len; |
91 | 127 | ||
@@ -98,36 +134,178 @@ static int w1_send_read_reply(struct w1_slave *sl, struct cn_msg *msg, | |||
98 | return err; | 134 | return err; |
99 | } | 135 | } |
100 | 136 | ||
101 | static int w1_process_command_slave(struct w1_slave *sl, struct cn_msg *msg, | 137 | static int w1_process_command_io(struct w1_master *dev, struct cn_msg *msg, |
102 | struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd) | 138 | struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd) |
103 | { | 139 | { |
104 | int err = 0; | 140 | int err = 0; |
105 | 141 | ||
106 | dev_dbg(&sl->master->dev, "%s: %02x.%012llx.%02x: cmd=%02x, len=%u.\n", | 142 | switch (cmd->cmd) { |
107 | __func__, sl->reg_num.family, (unsigned long long)sl->reg_num.id, sl->reg_num.crc, | 143 | case W1_CMD_TOUCH: |
108 | cmd->cmd, cmd->len); | 144 | w1_touch_block(dev, cmd->data, cmd->len); |
145 | w1_send_read_reply(msg, hdr, cmd); | ||
146 | break; | ||
147 | case W1_CMD_READ: | ||
148 | w1_read_block(dev, cmd->data, cmd->len); | ||
149 | w1_send_read_reply(msg, hdr, cmd); | ||
150 | break; | ||
151 | case W1_CMD_WRITE: | ||
152 | w1_write_block(dev, cmd->data, cmd->len); | ||
153 | break; | ||
154 | default: | ||
155 | err = -EINVAL; | ||
156 | break; | ||
157 | } | ||
158 | |||
159 | return err; | ||
160 | } | ||
161 | |||
162 | static int w1_process_command_master(struct w1_master *dev, struct cn_msg *req_msg, | ||
163 | struct w1_netlink_msg *req_hdr, struct w1_netlink_cmd *req_cmd) | ||
164 | { | ||
165 | int err = -EINVAL; | ||
166 | struct cn_msg *msg; | ||
167 | struct w1_netlink_msg *hdr; | ||
168 | struct w1_netlink_cmd *cmd; | ||
169 | |||
170 | msg = kzalloc(PAGE_SIZE, GFP_KERNEL); | ||
171 | if (!msg) | ||
172 | return -ENOMEM; | ||
173 | |||
174 | msg->id = req_msg->id; | ||
175 | msg->seq = req_msg->seq; | ||
176 | msg->ack = 0; | ||
177 | msg->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd); | ||
178 | |||
179 | hdr = (struct w1_netlink_msg *)(msg + 1); | ||
180 | cmd = (struct w1_netlink_cmd *)(hdr + 1); | ||
181 | |||
182 | hdr->type = W1_MASTER_CMD; | ||
183 | hdr->id = req_hdr->id; | ||
184 | hdr->len = sizeof(struct w1_netlink_cmd); | ||
185 | |||
186 | cmd->cmd = req_cmd->cmd; | ||
187 | cmd->len = 0; | ||
109 | 188 | ||
110 | switch (cmd->cmd) { | 189 | switch (cmd->cmd) { |
111 | case W1_CMD_READ: | 190 | case W1_CMD_SEARCH: |
112 | w1_read_block(sl->master, cmd->data, cmd->len); | 191 | case W1_CMD_ALARM_SEARCH: |
113 | w1_send_read_reply(sl, msg, hdr, cmd); | 192 | err = w1_process_search_command(dev, msg, |
114 | break; | 193 | PAGE_SIZE - msg->len - sizeof(struct cn_msg)); |
115 | case W1_CMD_WRITE: | 194 | break; |
116 | w1_write_block(sl->master, cmd->data, cmd->len); | 195 | case W1_CMD_READ: |
117 | break; | 196 | case W1_CMD_WRITE: |
118 | case W1_CMD_SEARCH: | 197 | case W1_CMD_TOUCH: |
119 | case W1_CMD_ALARM_SEARCH: | 198 | err = w1_process_command_io(dev, req_msg, req_hdr, req_cmd); |
120 | w1_search_process(sl->master, | 199 | break; |
121 | (cmd->cmd == W1_CMD_ALARM_SEARCH)?W1_ALARM_SEARCH:W1_SEARCH); | 200 | case W1_CMD_RESET: |
122 | break; | 201 | err = w1_reset_bus(dev); |
123 | default: | 202 | break; |
124 | err = -1; | 203 | default: |
125 | break; | 204 | err = -EINVAL; |
205 | break; | ||
126 | } | 206 | } |
127 | 207 | ||
208 | kfree(msg); | ||
128 | return err; | 209 | return err; |
129 | } | 210 | } |
130 | 211 | ||
212 | static int w1_process_command_slave(struct w1_slave *sl, struct cn_msg *msg, | ||
213 | struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd) | ||
214 | { | ||
215 | dev_dbg(&sl->master->dev, "%s: %02x.%012llx.%02x: cmd=%02x, len=%u.\n", | ||
216 | __func__, sl->reg_num.family, (unsigned long long)sl->reg_num.id, | ||
217 | sl->reg_num.crc, cmd->cmd, cmd->len); | ||
218 | |||
219 | return w1_process_command_io(sl->master, msg, hdr, cmd); | ||
220 | } | ||
221 | |||
222 | static int w1_process_command_root(struct cn_msg *msg, struct w1_netlink_msg *mcmd) | ||
223 | { | ||
224 | struct w1_master *m; | ||
225 | struct cn_msg *cn; | ||
226 | struct w1_netlink_msg *w; | ||
227 | u32 *id; | ||
228 | |||
229 | if (mcmd->type != W1_LIST_MASTERS) { | ||
230 | printk(KERN_NOTICE "%s: msg: %x.%x, wrong type: %u, len: %u.\n", | ||
231 | __func__, msg->id.idx, msg->id.val, mcmd->type, mcmd->len); | ||
232 | return -EPROTO; | ||
233 | } | ||
234 | |||
235 | cn = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
236 | if (!cn) | ||
237 | return -ENOMEM; | ||
238 | |||
239 | cn->id.idx = CN_W1_IDX; | ||
240 | cn->id.val = CN_W1_VAL; | ||
241 | |||
242 | cn->seq = msg->seq; | ||
243 | cn->ack = 1; | ||
244 | cn->len = sizeof(struct w1_netlink_msg); | ||
245 | w = (struct w1_netlink_msg *)(cn + 1); | ||
246 | |||
247 | w->type = W1_LIST_MASTERS; | ||
248 | w->status = 0; | ||
249 | w->len = 0; | ||
250 | id = (u32 *)(w + 1); | ||
251 | |||
252 | mutex_lock(&w1_mlock); | ||
253 | list_for_each_entry(m, &w1_masters, w1_master_entry) { | ||
254 | if (cn->len + sizeof(*id) > PAGE_SIZE - sizeof(struct cn_msg)) { | ||
255 | cn_netlink_send(cn, 0, GFP_KERNEL); | ||
256 | cn->ack++; | ||
257 | cn->len = sizeof(struct w1_netlink_msg); | ||
258 | w->len = 0; | ||
259 | id = (u32 *)(w + 1); | ||
260 | } | ||
261 | |||
262 | *id = m->id; | ||
263 | w->len += sizeof(*id); | ||
264 | cn->len += sizeof(*id); | ||
265 | id++; | ||
266 | } | ||
267 | cn->ack = 0; | ||
268 | cn_netlink_send(cn, 0, GFP_KERNEL); | ||
269 | mutex_unlock(&w1_mlock); | ||
270 | |||
271 | kfree(cn); | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static int w1_netlink_send_error(struct cn_msg *rcmsg, struct w1_netlink_msg *rmsg, | ||
276 | struct w1_netlink_cmd *rcmd, int error) | ||
277 | { | ||
278 | struct cn_msg *cmsg; | ||
279 | struct w1_netlink_msg *msg; | ||
280 | struct w1_netlink_cmd *cmd; | ||
281 | |||
282 | cmsg = kzalloc(sizeof(*msg) + sizeof(*cmd) + sizeof(*cmsg), GFP_KERNEL); | ||
283 | if (!cmsg) | ||
284 | return -ENOMEM; | ||
285 | |||
286 | msg = (struct w1_netlink_msg *)(cmsg + 1); | ||
287 | cmd = (struct w1_netlink_cmd *)(msg + 1); | ||
288 | |||
289 | memcpy(cmsg, rcmsg, sizeof(*cmsg)); | ||
290 | cmsg->len = sizeof(*msg); | ||
291 | |||
292 | memcpy(msg, rmsg, sizeof(*msg)); | ||
293 | msg->len = 0; | ||
294 | msg->status = (short)-error; | ||
295 | |||
296 | if (rcmd) { | ||
297 | memcpy(cmd, rcmd, sizeof(*cmd)); | ||
298 | cmd->len = 0; | ||
299 | msg->len += sizeof(*cmd); | ||
300 | cmsg->len += sizeof(*cmd); | ||
301 | } | ||
302 | |||
303 | error = cn_netlink_send(cmsg, 0, GFP_KERNEL); | ||
304 | kfree(cmsg); | ||
305 | |||
306 | return error; | ||
307 | } | ||
308 | |||
131 | static void w1_cn_callback(void *data) | 309 | static void w1_cn_callback(void *data) |
132 | { | 310 | { |
133 | struct cn_msg *msg = data; | 311 | struct cn_msg *msg = data; |
@@ -144,6 +322,7 @@ static void w1_cn_callback(void *data) | |||
144 | 322 | ||
145 | dev = NULL; | 323 | dev = NULL; |
146 | sl = NULL; | 324 | sl = NULL; |
325 | cmd = NULL; | ||
147 | 326 | ||
148 | memcpy(&id, m->id.id, sizeof(id)); | 327 | memcpy(&id, m->id.id, sizeof(id)); |
149 | #if 0 | 328 | #if 0 |
@@ -155,15 +334,15 @@ static void w1_cn_callback(void *data) | |||
155 | break; | 334 | break; |
156 | } | 335 | } |
157 | 336 | ||
158 | if (!mlen) | ||
159 | goto out_cont; | ||
160 | |||
161 | if (m->type == W1_MASTER_CMD) { | 337 | if (m->type == W1_MASTER_CMD) { |
162 | dev = w1_search_master_id(m->id.mst.id); | 338 | dev = w1_search_master_id(m->id.mst.id); |
163 | } else if (m->type == W1_SLAVE_CMD) { | 339 | } else if (m->type == W1_SLAVE_CMD) { |
164 | sl = w1_search_slave(&id); | 340 | sl = w1_search_slave(&id); |
165 | if (sl) | 341 | if (sl) |
166 | dev = sl->master; | 342 | dev = sl->master; |
343 | } else { | ||
344 | err = w1_process_command_root(msg, m); | ||
345 | goto out_cont; | ||
167 | } | 346 | } |
168 | 347 | ||
169 | if (!dev) { | 348 | if (!dev) { |
@@ -171,6 +350,10 @@ static void w1_cn_callback(void *data) | |||
171 | goto out_cont; | 350 | goto out_cont; |
172 | } | 351 | } |
173 | 352 | ||
353 | err = 0; | ||
354 | if (!mlen) | ||
355 | goto out_cont; | ||
356 | |||
174 | mutex_lock(&dev->mutex); | 357 | mutex_lock(&dev->mutex); |
175 | 358 | ||
176 | if (sl && w1_reset_select_slave(sl)) { | 359 | if (sl && w1_reset_select_slave(sl)) { |
@@ -187,9 +370,12 @@ static void w1_cn_callback(void *data) | |||
187 | } | 370 | } |
188 | 371 | ||
189 | if (sl) | 372 | if (sl) |
190 | w1_process_command_slave(sl, msg, m, cmd); | 373 | err = w1_process_command_slave(sl, msg, m, cmd); |
191 | else | 374 | else |
192 | w1_process_command_master(dev, msg, m, cmd); | 375 | err = w1_process_command_master(dev, msg, m, cmd); |
376 | |||
377 | w1_netlink_send_error(msg, m, cmd, err); | ||
378 | err = 0; | ||
193 | 379 | ||
194 | cmd_data += cmd->len + sizeof(struct w1_netlink_cmd); | 380 | cmd_data += cmd->len + sizeof(struct w1_netlink_cmd); |
195 | mlen -= cmd->len + sizeof(struct w1_netlink_cmd); | 381 | mlen -= cmd->len + sizeof(struct w1_netlink_cmd); |
@@ -200,6 +386,8 @@ out_up: | |||
200 | atomic_dec(&sl->refcnt); | 386 | atomic_dec(&sl->refcnt); |
201 | mutex_unlock(&dev->mutex); | 387 | mutex_unlock(&dev->mutex); |
202 | out_cont: | 388 | out_cont: |
389 | if (!cmd || err) | ||
390 | w1_netlink_send_error(msg, m, cmd, err); | ||
203 | msg->len -= sizeof(struct w1_netlink_msg) + m->len; | 391 | msg->len -= sizeof(struct w1_netlink_msg) + m->len; |
204 | m = (struct w1_netlink_msg *)(((u8 *)m) + sizeof(struct w1_netlink_msg) + m->len); | 392 | m = (struct w1_netlink_msg *)(((u8 *)m) + sizeof(struct w1_netlink_msg) + m->len); |
205 | 393 | ||
@@ -209,11 +397,6 @@ out_cont: | |||
209 | if (err == -ENODEV) | 397 | if (err == -ENODEV) |
210 | err = 0; | 398 | err = 0; |
211 | } | 399 | } |
212 | #if 0 | ||
213 | if (err) { | ||
214 | printk("%s: malformed message. Dropping.\n", __func__); | ||
215 | } | ||
216 | #endif | ||
217 | } | 400 | } |
218 | 401 | ||
219 | int w1_init_netlink(void) | 402 | int w1_init_netlink(void) |