Commit | Line | Data |
---|---|---|
89da1ecf SO |
1 | /* |
2 | * IrDA netlink layer, for stack configuration. | |
3 | * | |
22e1fb25 | 4 | * Copyright (c) 2007 Samuel Ortiz <samuel@sortiz.org> |
89da1ecf SO |
5 | * |
6 | * Partly based on the 802.11 nelink implementation | |
7 | * (see net/wireless/nl80211.c) which is: | |
8 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License version 2 as | |
12 | * published by the Free Software Foundation. | |
13 | * | |
14 | */ | |
15 | ||
16 | #include <linux/socket.h> | |
17 | #include <linux/irda.h> | |
881d966b | 18 | #include <net/net_namespace.h> |
89da1ecf SO |
19 | #include <net/sock.h> |
20 | #include <net/irda/irda.h> | |
21 | #include <net/irda/irlap.h> | |
22 | #include <net/genetlink.h> | |
23 | ||
24 | ||
25 | ||
26 | static struct genl_family irda_nl_family = { | |
27 | .id = GENL_ID_GENERATE, | |
28 | .name = IRDA_NL_NAME, | |
29 | .hdrsize = 0, | |
30 | .version = IRDA_NL_VERSION, | |
31 | .maxattr = IRDA_NL_CMD_MAX, | |
32 | }; | |
33 | ||
881d966b | 34 | static struct net_device * ifname_to_netdev(struct net *net, struct genl_info *info) |
89da1ecf SO |
35 | { |
36 | char * ifname; | |
37 | ||
38 | if (!info->attrs[IRDA_NL_ATTR_IFNAME]) | |
39 | return NULL; | |
40 | ||
41 | ifname = nla_data(info->attrs[IRDA_NL_ATTR_IFNAME]); | |
42 | ||
43 | IRDA_DEBUG(5, "%s(): Looking for %s\n", __FUNCTION__, ifname); | |
44 | ||
881d966b | 45 | return dev_get_by_name(net, ifname); |
89da1ecf SO |
46 | } |
47 | ||
48 | static int irda_nl_set_mode(struct sk_buff *skb, struct genl_info *info) | |
49 | { | |
50 | struct net_device * dev; | |
51 | struct irlap_cb * irlap; | |
52 | u32 mode; | |
53 | ||
54 | if (!info->attrs[IRDA_NL_ATTR_MODE]) | |
55 | return -EINVAL; | |
56 | ||
57 | mode = nla_get_u32(info->attrs[IRDA_NL_ATTR_MODE]); | |
58 | ||
59 | IRDA_DEBUG(5, "%s(): Switching to mode: %d\n", __FUNCTION__, mode); | |
60 | ||
881d966b | 61 | dev = ifname_to_netdev(&init_net, info); |
89da1ecf SO |
62 | if (!dev) |
63 | return -ENODEV; | |
64 | ||
65 | irlap = (struct irlap_cb *)dev->atalk_ptr; | |
66 | if (!irlap) { | |
67 | dev_put(dev); | |
68 | return -ENODEV; | |
69 | } | |
70 | ||
71 | irlap->mode = mode; | |
72 | ||
73 | dev_put(dev); | |
74 | ||
75 | return 0; | |
76 | } | |
77 | ||
78 | static int irda_nl_get_mode(struct sk_buff *skb, struct genl_info *info) | |
79 | { | |
80 | struct net_device * dev; | |
81 | struct irlap_cb * irlap; | |
82 | struct sk_buff *msg; | |
83 | void *hdr; | |
84 | int ret = -ENOBUFS; | |
85 | ||
881d966b | 86 | dev = ifname_to_netdev(&init_net, info); |
89da1ecf SO |
87 | if (!dev) |
88 | return -ENODEV; | |
89 | ||
90 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | |
91 | if (!msg) { | |
92 | dev_put(dev); | |
93 | return -ENOMEM; | |
94 | } | |
95 | ||
96 | irlap = (struct irlap_cb *)dev->atalk_ptr; | |
97 | if (!irlap) { | |
98 | ret = -ENODEV; | |
99 | goto err_out; | |
100 | } | |
101 | ||
102 | hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq, | |
103 | &irda_nl_family, 0, IRDA_NL_CMD_GET_MODE); | |
104 | if (IS_ERR(hdr)) { | |
105 | ret = PTR_ERR(hdr); | |
106 | goto err_out; | |
107 | } | |
108 | ||
109 | if(nla_put_string(msg, IRDA_NL_ATTR_IFNAME, | |
22117ea4 | 110 | dev->name)) |
89da1ecf SO |
111 | goto err_out; |
112 | ||
113 | if(nla_put_u32(msg, IRDA_NL_ATTR_MODE, irlap->mode)) | |
114 | goto err_out; | |
115 | ||
116 | genlmsg_end(msg, hdr); | |
117 | ||
118 | return genlmsg_unicast(msg, info->snd_pid); | |
119 | ||
120 | err_out: | |
121 | nlmsg_free(msg); | |
122 | dev_put(dev); | |
123 | ||
124 | return ret; | |
125 | } | |
126 | ||
127 | static struct nla_policy irda_nl_policy[IRDA_NL_ATTR_MAX + 1] = { | |
128 | [IRDA_NL_ATTR_IFNAME] = { .type = NLA_NUL_STRING, | |
129 | .len = IFNAMSIZ-1 }, | |
130 | [IRDA_NL_ATTR_MODE] = { .type = NLA_U32 }, | |
131 | }; | |
132 | ||
133 | static struct genl_ops irda_nl_ops[] = { | |
134 | { | |
135 | .cmd = IRDA_NL_CMD_SET_MODE, | |
136 | .doit = irda_nl_set_mode, | |
137 | .policy = irda_nl_policy, | |
138 | .flags = GENL_ADMIN_PERM, | |
139 | }, | |
140 | { | |
141 | .cmd = IRDA_NL_CMD_GET_MODE, | |
142 | .doit = irda_nl_get_mode, | |
143 | .policy = irda_nl_policy, | |
144 | /* can be retrieved by unprivileged users */ | |
145 | }, | |
146 | ||
147 | }; | |
148 | ||
149 | int irda_nl_register(void) | |
150 | { | |
151 | int err, i; | |
152 | ||
153 | err = genl_register_family(&irda_nl_family); | |
154 | if (err) | |
155 | return err; | |
156 | ||
157 | for (i = 0; i < ARRAY_SIZE(irda_nl_ops); i++) { | |
158 | err = genl_register_ops(&irda_nl_family, &irda_nl_ops[i]); | |
159 | if (err) | |
160 | goto err_out; | |
161 | } | |
162 | return 0; | |
163 | err_out: | |
164 | genl_unregister_family(&irda_nl_family); | |
165 | return err; | |
166 | } | |
167 | ||
168 | void irda_nl_unregister(void) | |
169 | { | |
170 | genl_unregister_family(&irda_nl_family); | |
171 | } |