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